Solving a Production DataGrid Width Issue in a Docker-Deployed React Application

Solving a Production DataGrid Width Issue in a Docker-Deployed React Application

The Problem Surfaces

During a routine check of my production ERP system, I noticed an error appearing repeatedly in the browser console when navigating to the employee management page:

errorTracking.ts:127 MUI X: useResizeContainer - The parent DOM element of the Data Grid has an empty width.
Please make sure that this element has an intrinsic width.
The grid displays with a width of 0px.

This wasn't just a console warning I could ignore. The DataGrid component, essential for displaying employee records, was failing to render properly. The timing of this discovery was particularly concerning since the application was already deployed in production using Docker containers, serving real users.

Initial Investigation

My first instinct was to understand the scope of the problem. I needed to determine whether this was a widespread issue or specific to certain components. I started by examining the project structure to locate where DataGrid components were being used:

find ./erp-frontend/src -type f \( -name "*.tsx" -o -name "*.ts" \) 2>/dev/null | grep -v node_modules | grep -i -E "(employee|grid|table|data)" | head -30

This command revealed several key files:

./erp-frontend/src/components/common/OptimizedDataGrid.tsx
./erp-frontend/src/components/common/LazyDataGrid.tsx
./erp-frontend/src/pages/hr/Employees.tsx
./erp-frontend/src/pages/hr/EmployeeDetail.tsx

I discovered that while my application had custom DataGrid wrappers (OptimizedDataGrid and LazyDataGrid), the problematic Employees page was using the MUI DataGrid directly.

Diving Deeper into the Component Structure

I examined the Employees component to understand how the DataGrid was being implemented:

cat ./erp-frontend/src/pages/hr/Employees.tsx | grep -A 80 "return ("

This revealed something interesting - defensive measures had already been attempted:

<Paper
  sx={{
    height: 600,
    width: '100%',
    minWidth: 300, // Defensive minimum width to prevent 0px scenarios
    '& .MuiDataGrid-root': {
      minWidth: 300, // Ensure DataGrid itself has minimum width
    }
  }}
>
  <DataGrid
    rows={employees}
    columns={columns}
    // ... other props
  />
</Paper>

I had previously tried to fix this issue by adding minimum width values, but the error persisted. This told me the problem was occurring at a higher level in the component hierarchy.

Tracing the Component Hierarchy

I needed to understand how the Employees component was being mounted within the application:

cat ./erp-frontend/src/App.tsx | grep -B 5 -A 15 -i "employee"

This showed me the routing structure:

<Route path="/hr">
  <Route path="employees" element={<Employees />} />
  <Route path="employees/:id" element={<EmployeeDetail />} />
</Route>

The Employees component was nested within multiple route levels, wrapped by a Layout component. This multi-level nesting was my next clue.

Identifying the Root Cause

I located and examined the Layout component:

find ./erp-frontend/src -type f -name "*ayout*.tsx"
cat ./erp-frontend/src/components/layout/Layout.tsx | head -150

The Layout component revealed the true source of the problem:

<Box
  component="main"
  sx={{
    flexGrow: 1,
    height: '100vh',
    overflow: 'auto',
    paddingTop: '64px',
    paddingLeft: sidebarOpen ? `${sidebarWidth}px` : 0,
    transition: theme.transitions.create(['padding-left'], {
      easing: theme.transitions.easing.sharp,
      duration: theme.transitions.duration.leavingScreen,
    }),
  }}
>
  <Box sx={{ p: 3 }}>
    <Outlet />
  </Box>
</Box>

The main content area relied on flexGrow: 1 without explicit width calculations. During the initial render cycle, particularly with the animated sidebar transitions, there was a moment where the browser's layout engine hadn't calculated the flex container's width, causing child components to receive 0px width.

Implementing the Solution

After creating a backup of the Layout component, I implemented a comprehensive fix that addressed the timing and calculation issues:

// ~/code/ltphongssvn/pcvn-fullstack/erp-frontend/src/components/layout/Layout.tsx
const Layout: React.FC = () => {
  const theme = useTheme();
  const { sidebarOpen, sidebarCollapsed } = useAppSelector((state) => state.ui);
  const sidebarWidth = sidebarCollapsed ? DRAWER_WIDTH_COLLAPSED : DRAWER_WIDTH;
  
  // State to track when layout dimensions are stable
  const [layoutReady, setLayoutReady] = useState(false);

  useEffect(() => {
    // Wait for next paint cycle before marking layout as ready
    const frameId = requestAnimationFrame(() => {
      setLayoutReady(true);
    });
    return () => cancelAnimationFrame(frameId);
  }, []);

  return (
    <Box sx={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>
      <Header />
      <Sidebar />
      <Box
        component="main"
        sx={{
          flexGrow: 1,
          paddingLeft: sidebarOpen ? `${sidebarWidth}px` : 0,
          // Critical: Explicit width calculation
          width: sidebarOpen ? `calc(100vw - ${sidebarWidth}px)` : '100vw',
          minWidth: 0,
          transition: theme.transitions.create(['padding-left', 'width'], {
            easing: theme.transitions.easing.sharp,
            duration: theme.transitions.duration.leavingScreen,
          }),
        }}
      >
        <Box sx={{ 
          p: 3,
          width: '100%',
          minWidth: 0,
          position: 'relative',
          opacity: layoutReady ? 1 : 0,
          transition: 'opacity 0.2s ease-in-out',
        }}>
          {layoutReady ? <Outlet /> : <Box sx={{ minHeight: '600px' }} />}
        </Box>
      </Box>
    </Box>
  );
};

The solution involved three key improvements:

  1. Explicit width calculation using calc(100vw - ${sidebarWidth}px) instead of relying solely on flexbox
  2. Layout stabilization with requestAnimationFrame to ensure dimensions are established before rendering children
  3. Defensive CSS properties including position: relative and minWidth: 0 to create a stable rendering context

Deploying the Fix in Production

Since my application was running in Docker containers, I needed to rebuild and redeploy. First, I checked the running containers:

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "NAME|frontend|erp"

Output showed all containers were running in production mode:

pcvn-erp-frontend-prod   Up About an hour (healthy)     0.0.0.0:3003->80/tcp
pcvn-erp-backend-prod    Up About an hour (healthy)     0.0.0.0:3002->3002/tcp

I identified the Docker Compose configuration being used:

docker compose ls

Output:

NAME                STATUS              CONFIG FILES
pcvn-fullstack      running(6)          docker-compose.prod-windows.yml

I then rebuilt the frontend container with my changes:

docker compose -f docker-compose.prod-windows.yml build frontend

The build process showed successful compilation:

✓ 14110 modules transformed.
✓ built in 26.52s

Finally, I recreated the container with the new image:

docker compose -f docker-compose.prod-windows.yml up -d frontend --force-recreate

Verification and Results

To verify the fix was deployed, I checked the container creation times:

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.CreatedAt}}" | grep frontend

The frontend container showed it was freshly created with my changes.

I then verified the built files were being served:

docker exec pcvn-erp-frontend-prod ls -la /usr/share/nginx/html/assets/ | grep "index-"

Output confirmed the new bundles were in place:

-rw-r--r--    1 nginx    nginx      1677330 Aug 24 23:29 index-bdEiauXI.js

Opening the browser console and navigating to /hr/employees showed clean logs with no DataGrid width errors. The comprehensive monitoring output displayed normal APM metrics, WebSocket connections, and analytics events - but critically, no MUI resize container warnings.

Reflection on the Experience

This debugging journey reinforced several important lessons for me. First, production issues often stem from subtle timing problems rather than obvious bugs. I had already attempted to fix the issue with minimum width values, but hadn't identified the root cause in the parent Layout component.

Second, systematic investigation beats random attempts at fixes. By methodically tracing from the error message through the component hierarchy to the Layout wrapper, I could identify the actual source of the problem rather than just treating symptoms.

Third, Docker deployments add complexity but also provide safety. The ability to rebuild and redeploy specific services without affecting the entire stack allowed me to implement the fix with minimal disruption to the production environment.

The solution I implemented isn't just a quick patch - it's a robust architectural improvement that prevents an entire class of width calculation issues. By ensuring the layout provides stable dimensions before child components render, I've created a foundation that will prevent similar issues as the application grows.

This experience reminded me that even in production environments, with careful investigation and methodical problem-solving, complex rendering issues can be resolved safely and effectively. The key is understanding not just what's breaking, but why it's breaking, and addressing the root cause rather than just the visible symptoms.


If you enjoyed this article, you can also find it published on LinkedIn and Medium.