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":
justifyContentcontrols the vertical axisalignItemscontrols 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 →