Remotion staticFile and Public Assets: The Complete Guide
Remotion staticFile and Public Assets: The Complete Guide
TL;DR
Local assets go in the public/ folder and you reference them with staticFile() — a helper that resolves a path relative to public/ into a URL valid in both the Studio preview and during rendering. Relative or imported paths work in dev but break during render, which is why a logo can vanish from the output MP4. You omit the public/ prefix (staticFile('logo.png')), pass remote URLs directly without staticFile(), and can point Remotion at a different folder with setPublicDir() or --public-dir.
One of the first things every Remotion developer hits is an asset that shows up in the browser preview but vanishes — or throws an error — during rendering. The image looks fine while you build, then the rendered MP4 is missing your logo. The cause is almost always the same: the asset was referenced with a relative or imported path instead of staticFile().
The rule to internalize is short: local assets live in the public/ folder, and you reference them with staticFile(). That single helper builds a URL that works identically in the Studio preview and during server-side rendering. This guide explains why staticFile() exists, how to use it with every media component, how to organize your assets, and how to change the public directory when you need to.
Why staticFile Exists
In a normal web app, you might import logo from "./logo.png" and let the bundler resolve it. Remotion renders differently: it runs a server that serves your project and captures frames through headless Chromium, and during distributed rendering the code runs in an environment where relative filesystem paths do not point where you expect.
staticFile() solves this by resolving a path relative to the public/ folder into a URL that is valid in every context — local preview, local render, and cloud rendering on Lambda or Cloud Run. You get one consistent way to reference an asset that never breaks between preview and render.
Full API reference: remotion.dev/docs/staticfile.
The public Folder
Create a public/ folder at the root of your Remotion project and put your assets in it:
my-video/
├── public/
│ ├── logo.png
│ ├── voiceover.mp3
│ ├── broll.mp4
│ └── fonts/
│ └── Brand.woff2
├── src/
│ └── ...
└── package.json
Anything inside public/ is servable. You reference it by passing the path relative to public/ — not including public/ itself:
import { staticFile } from "remotion";
staticFile("logo.png"); // → public/logo.png
staticFile("fonts/Brand.woff2"); // → public/fonts/Brand.woff2
Note that you do not write staticFile("public/logo.png") — the public/ prefix is implied.
Using staticFile With Media Components
staticFile() produces a URL, so it works anywhere a src is expected. Here is each media type.
Images with the <Img> component:
import { Img, staticFile } from "remotion";
export const Logo = () => (
<Img src={staticFile("logo.png")} style={{ width: 200 }} />
);
Video with the <Video> component from @remotion/media (or <OffthreadVideo> from remotion — see the full guide to embedding video with OffthreadVideo):
import { Video } from "@remotion/media";
import { staticFile } from "remotion";
export const Clip = () => (
<Video src={staticFile("broll.mp4")} muted style={{ opacity: 0.5 }} />
);
Audio with the <Audio> component from @remotion/media:
import { Audio } from "@remotion/media";
import { staticFile } from "remotion";
export const Voice = () => <Audio src={staticFile("voiceover.mp3")} />;
Fonts loaded via the FontFace API also take a staticFile() URL — see loading custom fonts in Remotion for the full pattern. Pair this with delayRender() so the render waits for the font to finish loading before capturing frames.
Remote URLs Do Not Need staticFile
staticFile() is only for local assets in your public/ folder. If your asset already lives at a full URL — a CDN, an S3 bucket, an API — pass that URL directly:
import { Img } from "remotion";
export const Remote = () => (
<Img src="https://cdn.example.com/logo.png" />
);
Mixing the two is fine: local brand assets via staticFile(), dynamic per-render assets via the URLs your data provides.
Dynamic Paths
Because staticFile() is just a function returning a string, you can build paths from props or data — useful for templated and personalized video generated at scale where each render uses a different asset:
import { Img, staticFile } from "remotion";
export const Avatar: React.FC<{ userId: string }> = ({ userId }) => (
<Img src={staticFile(`avatars/${userId}.png`)} />
);
Make sure the file actually exists in public/avatars/. If a path might contain characters that are not URL-safe, encode the segment (for example with encodeURIComponent) before passing it, so the resolved URL stays valid.
Changing the Public Directory
By default Remotion uses the public/ folder at your project root. When you need a different location — a monorepo layout, or reusing a shared assets folder — you can point Remotion at another directory. In code you set it explicitly, and the CLI accepts a --public-dir flag on render commands:
import { setPublicDir } from "@remotion/renderer";
setPublicDir("./shared-assets");
Changing the public directory is useful when several compositions share one asset library, or when you extract a project into a package. Keep it consistent across preview and render so staticFile() resolves the same way in both.
Common Mistakes
| Mistake | Result | Fix |
|---|---|---|
import logo from "./logo.png" | Works in dev, may fail in render | Move to public/, use staticFile() |
staticFile("public/logo.png") | Wrong path (double public/) | Drop the prefix: staticFile("logo.png") |
Asset outside public/ | Not servable | Place it inside public/ |
Raw HTML <video>/<audio> tag | Out of sync during render | Use @remotion/media / <OffthreadVideo> |
| Unencoded special characters in path | Broken URL | Encode dynamic segments |
Build Faster with RenderComp
Getting assets, fonts, and media wired correctly is exactly the kind of setup work that slows a project down. RenderComp provides production-ready Remotion templates — intros, lower thirds, social formats, data visualizations, and more — built with proper staticFile() asset handling already in place and typed props ready for your render pipeline. Drop in your brand assets and render.
Browse the collection at rendercomp.com and start rendering on day one.
Frequently asked questions
What does staticFile do in Remotion?
It converts a path relative to your project's public/ folder into a URL that works in both the Studio preview and during rendering — locally and in the cloud. It is the correct way to reference local images, video, audio, and fonts.
Why do my images work in preview but not in the render?
They were referenced with a relative or imported path that resolves in development but not during rendering. Move the asset into public/ and reference it with staticFile().
Do I include the public/ prefix in the staticFile path?
No. staticFile() resolves relative to the public/ folder, so you pass staticFile('logo.png'), not staticFile('public/logo.png'). The public/ prefix is implied.
Can I use staticFile for remote URLs?
No — staticFile() is only for local assets in public/. For assets already at a full URL (a CDN, S3, or an API), pass the URL directly to the component's src.
Can I build staticFile paths dynamically?
Yes. staticFile() returns a string, so you can interpolate props or data into the path — for example a per-user avatar filename built from a userId. Make sure the file exists in public/, and encode any characters that are not URL-safe before passing them in.
How do I change the public folder location?
Use setPublicDir() from @remotion/renderer in code, or the --public-dir flag on render commands. This helps with monorepos or shared asset libraries. Keep it consistent between preview and render so staticFile() resolves the same way.
Now available
Get 1,000+ Remotion Templates
Pay once — no subscription. Lifetime updates. TypeScript-first.
View pricing →