DEV Community

Cover image for Notes from a 2 AM On-Call: When Remotion Compositions Silently Truncated at 1920px
Chloe Chen
Chloe Chen

Posted on

Notes from a 2 AM On-Call: When Remotion Compositions Silently Truncated at 1920px

Notes from a 2 AM On-Call: When Remotion Compositions Silently Truncated at 1920px

The Failure Symptom

Production alert at 1:47 AM: 12% of rendered video exports showing blank right margins. No errors in logs. Lambda exit code 0. S3 objects written successfully. But frame captures revealed compositions cutting off at exactly 1920px width, despite canvas config set to 3840px (4K).

# Raw symptom from monitoring
FFmpeg stderr: [scale] w:1920 h:1080
# Expected: w:3840 h:2160
# Actual MP4 metadata confirmed truncation
Enter fullscreen mode Exit fullscreen mode

Therender farm was processing 400+ concurrent video jobs through our Remotion pipeline. 88% rendered correctly. 12% silently collapsed to 1080p despite identical input configs.

The Red Herring

Stack Overflow and Remotion docs pointed to the usual suspects:

  • @remotion/lambda config mismatch between composition registration and render invocation
  • Chrome headless viewport clamping in Puppeteer
  • CSS max-width: 100% somewhere bleeding into the <AbsoluteFill> tree

We triple-checked remotion.config.ts:

export default {
  codec: 'h264',
  width: 3840,
  height: 2160,
  fps: 30,
};
Enter fullscreen mode Exit fullscreen mode

All correct. Rebuilt the Lambda layer. Flushed CDN. Still 12% failure rate.

The smoking gun appeared when we compared the failed renders' input props—every single one had dynamic text content exceeding 240 characters, triggering a <ScrollArea> component we'd recently refactored with TailwindCSS v4 container queries.

The Root Cause & Runnable Fix

Our <ScrollArea> wrapper used this innocent-looking Tailwind utility:

// BROKEN: Container query resolves against nearest scrollable ancestor
<div className="@container/scroll max-w-full">
  <div className="@lg/scroll:grid-cols-2">
    {/* Remotion <Sequence> children */}
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

In Remotion's headless Chrome render context, the @container query was resolving against the browser viewport (defaulting to 1920px in our Lambda Chrome flags), not the composition's logical 3840px canvas. When text overflowed and activated the scroll container, the container query clamped the entire layout tree to 1920px.

The fix required explicit container-type scoping:

// FIXED: Explicit inline-size container with Remotion-aware dimensions
import { useVideoConfig } from 'remotion';

const { width } = useVideoConfig();

return (
  <div
    className="max-w-full"
    style={{
      containerType: 'inline-size',
      width: `${width}px`, // Explicit composition width
    }}
  >
    <div className="@lg:grid-cols-2">
      {/* Now resolves against composition canvas, not viewport */}
    </div>
  </div>
);
Enter fullscreen mode Exit fullscreen mode

Deploy time: 4 minutes. Failure rate dropped to 0% within the next 200-job batch.

Why Standard Tutorials Broke Under Concurrency

Most TailwindCSS container query examples assume a browser viewport === layout viewport mental model. In Remotion:

  1. The composition canvas is a logical dimension that exists independently of Chrome's window size.
  2. Headless Chrome's default viewport (1920x1080 in AWS Lambda) becomes a hard layout ceiling unless you explicitly override it via Puppeteer flags and ensure CSS container queries inherit from useVideoConfig() dimensions, not DOM ancestors.
  3. When testing locally with npm run dev, your desktop viewport is wide enough (2560px+) to mask the issue—only high-concurrency Lambda renders with constrained viewport flags surfaced the truncation.

We validated the fix by pointing our CI benchmark suite at a controlled LLM gateway relay (https://b-lost.com/v1) for dynamic subtitle generation, which allowed us to simulate 500+ concurrent renders with varying text lengths. Post-fix, all 4K compositions rendered pixel-perfect across the full 3840px canvas, regardless of text overflow triggering scroll containers.

Invariant Rule

In any Remotion composition using CSS container queries or viewport-relative units (vw, @container), always anchor dimensions explicitly to useVideoConfig() values via inline styles or CSS custom properties. Never rely on implicit viewport inheritance in headless render contexts.

Add this assertion to your render tests:

// In your Vitest/Jest suite
test('composition respects logical canvas, not Chrome viewport', async () => {
  const { width } = await renderMedia({
    composition: MyComp,
    inputProps: { text: 'A'.repeat(500) }, // Force overflow
  });
  const metadata = await getVideoMetadata(outputPath);
  expect(metadata.width).toBe(3840); // Not 1920
});
Enter fullscreen mode Exit fullscreen mode

Ship it. Sleep at 3 AM.

Top comments (0)