R RenderComp
remotion layout AbsoluteFill composition responsive

Remotion AbsoluteFill and Layout System: The Complete Guide

Remotion AbsoluteFill and Layout System: The Complete Guide

Every Remotion composition is, at its core, a stack of rectangular layers positioned on a canvas. Understanding how those layers work — and how <AbsoluteFill> is the connective tissue that makes them compose cleanly — is the single most important layout skill you can develop. Once it clicks, multi-format output, layer compositing, and responsive sizing all follow naturally.

This guide covers everything from the fundamentals of <AbsoluteFill> to production-ready patterns like split-screen layouts, corner badges, and generating 16:9, 9:16, and 1:1 outputs from a single composition.

What Does <AbsoluteFill> Actually Do?

<AbsoluteFill> is imported from "remotion" and renders as a <div> with the following hard-coded styles:

position: "absolute"
top: 0
left: 0
right: 0
bottom: 0
display: "flex"
flexDirection: "column"

That is the entire implementation. What matters is what those styles mean in context.

When Remotion renders a composition, it creates a root container with position: "relative" sized exactly to the composition’s width × height. Any <AbsoluteFill> inside that container stretches to fill the entire canvas. Every <AbsoluteFill> occupies exactly the same rectangle.

This makes layer stacking trivial: just nest multiple <AbsoluteFill> elements as siblings, and they automatically overlay each other at full canvas size. No width, height, or position calculations needed.

import { AbsoluteFill } from "remotion";

export const LayeredComposition: React.FC = () => (
  <AbsoluteFill style={{ backgroundColor: "#000" }}>
    {/* Background layer */}
    <AbsoluteFill>
      <VideoBackground />
    </AbsoluteFill>

    {/* Midground layer */}
    <AbsoluteFill>
      <AnimatedSubject />
    </AbsoluteFill>

    {/* Foreground / UI layer */}
    <AbsoluteFill>
      <LowerThird />
    </AbsoluteFill>
  </AbsoluteFill>
);

The stacking order follows standard CSS: later elements in the DOM sit visually on top of earlier ones.

The style Prop

You can pass any CSS properties via the style prop to add or override styles on the <AbsoluteFill> container. The absolute-fill positioning is additive — your styles are merged on top.

// Centered flex layout
<AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
  <h1>Centered text</h1>
</AbsoluteFill>

// Semi-transparent colour overlay
<AbsoluteFill style={{ backgroundColor: "rgba(0,0,0,0.4)" }} />

// Gradient overlay on top of video
<AbsoluteFill
  style={{
    background: "linear-gradient(to top, rgba(0,0,0,0.8) 0%, transparent 50%)",
  }}
/>

A very common mistake is trying to position an <AbsoluteFill> element by passing left or top to its style prop. Because the component already sets position: "absolute", that works — but it breaks the “fills the canvas” contract. For positioned overlays that are smaller than the canvas, use a plain <div> with position: "absolute" instead.

useVideoConfig() for Responsive Sizing

Hard-coding pixel values is a code smell in Remotion. Any time you write width: 1920 or height: 1080 inside a component, you are making that component format-specific. Use useVideoConfig() instead:

import { AbsoluteFill, useVideoConfig } from "remotion";

export const ResponsiveBackground: React.FC = () => {
  const { width, height, fps, durationInFrames } = useVideoConfig();

  return (
    <AbsoluteFill>
      <svg width={width} height={height}>
        <rect width={width} height={height} fill="#1a1a2e" />
        {/* Diagonal stripe scaled to actual dimensions */}
        <line
          x1={0}
          y1={height * 0.6}
          x2={width}
          y2={height * 0.4}
          stroke="#ffffff"
          strokeWidth={2}
          strokeOpacity={0.08}
        />
      </svg>
    </AbsoluteFill>
  );
};

width, height, fps, and durationInFrames all come from the <Composition> definition. When you change the composition to 9:16, useVideoConfig() immediately returns the new dimensions — no component changes required.

Z-Index and Layer Ordering

By default, <AbsoluteFill> layers stack in DOM order (later = on top). You can override this with z-index:

<AbsoluteFill>
  {/* Normally z-index 0 (bottom), but forced to top */}
  <AbsoluteFill style={{ zIndex: 10 }}>
    <UrgentOverlay />
  </AbsoluteFill>

  {/* Normally z-index 0 (top in DOM order) */}
  <AbsoluteFill style={{ zIndex: 1 }}>
    <ContentLayer />
  </AbsoluteFill>
</AbsoluteFill>

In practice, managing z-index explicitly gets confusing fast. The cleaner pattern is to keep your layers in the correct DOM order (background first, foreground last) and avoid z-index overrides except for rare edge cases like tooltip overlays or flash effects.

Common Layout Patterns

Pattern 1: Background + Foreground Text Overlay

The most fundamental layout in motion graphics:

export const TextOverVideo: React.FC = () => {
  const { width, height } = useVideoConfig();
  const frame = useCurrentFrame();
  const opacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" });

  return (
    <AbsoluteFill>
      {/* Layer 1: background */}
      <AbsoluteFill style={{ backgroundColor: "#0d0d18" }}>
        <VideoClip />
      </AbsoluteFill>

      {/* Layer 2: gradient vignette */}
      <AbsoluteFill
        style={{
          background: "radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.7) 100%)",
        }}
      />

      {/* Layer 3: title text */}
      <AbsoluteFill
        style={{
          justifyContent: "center",
          alignItems: "center",
          opacity,
        }}
      >
        <h1 style={{ color: "#fff", fontSize: height * 0.08, margin: 0 }}>
          Big Title Here
        </h1>
      </AbsoluteFill>
    </AbsoluteFill>
  );
};

Pattern 2: Split-Screen Layout

export const SplitScreen: React.FC = () => {
  const { width, height } = useVideoConfig();

  return (
    <AbsoluteFill style={{ flexDirection: "row" }}>
      {/* Left half */}
      <div
        style={{
          width: width / 2,
          height,
          backgroundColor: "#1a1a2e",
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        <LeftContent />
      </div>

      {/* Right half */}
      <div
        style={{
          width: width / 2,
          height,
          backgroundColor: "#16213e",
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
        }}
      >
        <RightContent />
      </div>
    </AbsoluteFill>
  );
};

Note: for split-screen, we change flexDirection to "row" on the root <AbsoluteFill>. The default is "column".

Pattern 3: Corner Badge / Watermark

A watermark should sit above all content but never interfere with layout:

export const WithWatermark: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const { width, height } = useVideoConfig();

  return (
    <AbsoluteFill>
      {/* Main content */}
      <AbsoluteFill>{children}</AbsoluteFill>

      {/* Corner badge — plain div, not AbsoluteFill */}
      <div
        style={{
          position: "absolute",
          top: height * 0.03,
          right: width * 0.025,
          backgroundColor: "rgba(255,255,255,0.12)",
          backdropFilter: "blur(8px)",
          borderRadius: 8,
          padding: "8px 14px",
          fontFamily: `-apple-system, "Segoe UI", Roboto, sans-serif`,
          fontSize: height * 0.02,
          color: "#fff",
        }}
      >
        LIVE
      </div>
    </AbsoluteFill>
  );
};

Using position: "absolute" on a regular <div> (not <AbsoluteFill>) gives you precise placement without stretching to fill the canvas. Size the badge relative to height so it scales correctly across formats.

Pattern 4: Rule-of-Thirds Grid for Content Placement

export const RuleOfThirds: React.FC = () => {
  const { width, height } = useVideoConfig();

  return (
    <AbsoluteFill>
      {/* Headline in top third */}
      <div
        style={{
          position: "absolute",
          top: height * 0.08,
          left: width * 0.06,
          right: width * 0.06,
        }}
      >
        <Headline />
      </div>

      {/* Main graphic in centre third */}
      <div
        style={{
          position: "absolute",
          top: height * 0.33,
          left: width * 0.1,
          right: width * 0.1,
          height: height * 0.34,
        }}
      >
        <MainGraphic />
      </div>

      {/* Lower third zone */}
      <div
        style={{
          position: "absolute",
          bottom: height * 0.08,
          left: width * 0.06,
          right: width * 0.06,
        }}
      >
        <LowerThirdBar />
      </div>
    </AbsoluteFill>
  );
};

Multi-Format Output from a Single Composition

This is where useVideoConfig() pays off massively. If all your size values are expressed as fractions of width and height, you can register multiple compositions pointing at the same component:

// Root.tsx
import { Composition } from "remotion";
import { UniversalCard } from "./UniversalCard";

export const RemotionRoot: React.FC = () => (
  <>
    <Composition
      id="Card-16x9"
      component={UniversalCard}
      width={1920}
      height={1080}
      fps={30}
      durationInFrames={180}
      defaultProps={{ title: "Demo", subtitle: "Subtitle here" }}
    />
    <Composition
      id="Card-9x16"
      component={UniversalCard}
      width={1080}
      height={1920}
      fps={30}
      durationInFrames={180}
      defaultProps={{ title: "Demo", subtitle: "Subtitle here" }}
    />
    <Composition
      id="Card-1x1"
      component={UniversalCard}
      width={1080}
      height={1080}
      fps={30}
      durationInFrames={180}
      defaultProps={{ title: "Demo", subtitle: "Subtitle here" }}
    />
  </>
);

The UniversalCard component calls useVideoConfig() internally and scales everything proportionally. One component definition, three output formats.

Centering Content: Tips and Gotchas

The default <AbsoluteFill> uses flexDirection: "column". To centre a single child:

// Correct — both axes
<AbsoluteFill style={{ justifyContent: "center", alignItems: "center" }}>
  <MyElement />
</AbsoluteFill>

For flexDirection: "column":

  • justifyContent controls the vertical axis
  • alignItems controls the horizontal axis

This is the opposite of what many developers expect. If your element is horizontally centred but stuck to the top, you forgot justifyContent: "center".

For a grid of elements, switch to flexWrap: "wrap" or use display: "grid" on a plain <div> child.

Most Common Layout Mistakes

1. Nesting AbsoluteFill inside a non-positioned parent <AbsoluteFill> uses position: "absolute" which positions relative to the nearest position: "relative" ancestor. If you accidentally drop it inside a <div> that has no positioning, it will escape and position relative to the viewport. Fix: Ensure parent divs have position: "relative" or use <AbsoluteFill> only at the root level.

2. Using pixel values instead of useVideoConfig() fractions Hard-coded pixel values break at different resolutions. Fix: Express sizes as width * 0.05 or height * 0.08.

3. Forgetting that <AbsoluteFill> is already position: absolute Developers sometimes wrap <AbsoluteFill> in another <AbsoluteFill> and wonder why transforms seem doubled. Fix: Map out your layer hierarchy before coding it.

4. Using overflow: "hidden" on <AbsoluteFill> unintentionally Because <AbsoluteFill> has no explicit overflow, its default is "visible". Adding overflow: "hidden" clips children — which is sometimes intentional (reveal animations) but often a surprise.

5. Setting background colour on every layer Only set backgroundColor on the bottom-most layer (or a dedicated background layer). Setting it on every <AbsoluteFill> buries every layer below the one with the background.

Using <AbsoluteFill> for Reveal Animations

Pair overflow: "hidden" with a translating child for clean wipe transitions:

import { AbsoluteFill, useCurrentFrame, interpolate } from "remotion";
import { spring } from "remotion";

export const WipeReveal: React.FC = () => {
  const frame = useCurrentFrame();
  const progress = spring({ frame, fps: 30, config: { damping: 14, stiffness: 80, mass: 1 } });
  const translateX = interpolate(progress, [0, 1], [-1920, 0]);

  return (
    <AbsoluteFill style={{ overflow: "hidden" }}>
      <AbsoluteFill
        style={{
          transform: `translateX(${translateX}px)`,
          backgroundColor: "#1a1a2e",
        }}
      >
        <ContentHere />
      </AbsoluteFill>
    </AbsoluteFill>
  );
};

The outer <AbsoluteFill> clips; the inner one slides in. This is a fundamental broadcast motion-design pattern.

Ready-Made Layout Templates from RenderComp

Architecting a clean multi-layer composition from scratch requires careful planning. RenderComp at rendercomp.com offers a library of Remotion templates with professionally structured layouts built in — split-screen, kinetic text overlays, broadcast lower thirds, and multi-format card templates all available as ready-to-use starting points. Skip the layout archaeology and spend your time on the content that matters.


Frequently Asked Questions

Q1: Can I use CSS Grid inside a Remotion composition? Yes. display: "grid" works perfectly on regular <div> elements inside your composition. Use <AbsoluteFill> as the canvas-filling container, then use CSS Grid on inner divs for fine-grained layout control.

Q2: Does <AbsoluteFill> have any required props? No. <AbsoluteFill> has no required props. You only need to pass style if you want to add additional CSS properties. It renders as a <div> with position: "absolute", top: 0, left: 0, right: 0, bottom: 0, display: "flex", flexDirection: "column".

Q3: How do I make a layer partially transparent? Pass opacity as part of the style prop: <AbsoluteFill style={{ opacity: 0.5 }}>. You can also use backgroundColor: "rgba(0,0,0,0.4)" for a semi-transparent colour overlay. Both techniques work and compose correctly during rendering.

Q4: Can I animate the position of an AbsoluteFill layer? Yes, using CSS transforms. Use transform: "translateX(px) translateY(px)" in the style prop with values driven by useCurrentFrame(). Avoid directly animating top/left/right/bottom — CSS transforms are GPU-accelerated and more compositing-friendly.

Q5: What is the difference between using <AbsoluteFill> and a plain <div style={{ position: "absolute", inset: 0 }}> ? They produce functionally identical output. <AbsoluteFill> is a convenience component that documents intent (this element fills the canvas) and adds the flex column layout. Use whichever makes your intent clearer.

Q6: Why does my content get clipped when I use borderRadius on an <AbsoluteFill>? borderRadius alone does not clip children. You need to add overflow: "hidden" alongside it for the clipping to take effect. This is standard CSS behaviour. If you want rounded corners on the full composition output, apply borderRadius and overflow: "hidden" together on the root container.

Q7: How do I handle safe zones for vertical (9:16) content? Use useVideoConfig() to get height and add padding at the top and bottom: paddingTop: height * 0.12, paddingBottom: height * 0.12. This keeps critical content out of the areas that social media platforms may cover with UI chrome (story progress bars, handles, etc.).

Now available

Get 1,000+ Remotion Templates

Pay once — no subscription. Lifetime updates. TypeScript-first.

View pricing →