Back to Remotion

Submit an Element

packages/docs/elements/submit-an-element.mdx

4.0.4875.1 KB
Original Source

Submit an Element

Have a small, remixable Remotion Element? We'd like to see it.

Follow this checklist from top to bottom when preparing an Element.

1. Choose a focused idea

A strong Element can be previewed, copied into a Remotion project, and used as a video building block.

Good Elements are:

  • Focused on one visual idea, technique, or workflow
  • Copy-pastable and useful in more than one Remotion project
  • Remixable
  • HTML, CSS, and React-first
  • Free of unexpected global styles, layout side effects, and fullscreen assumptions unless it is explicitly a background
  • Building blocks, not compositions: The hosted docs page provides the preview size, frame rate, and duration.

If two variants are worth showing, make two separate Element pages instead of adding a variant prop.

2. Create the Element files

Start from the reusable template in packages/docs/elements-template/.

Add:

  • An Element folder in packages/docs/elements/<category>/<slug>/
  • An index.mdx page
  • One self-contained TSX source file exporting a React component

For example:

tsx
export const MyElement = () => {
  return <div style={{width: '100%', height: '100%'}}>...</div>;
};

3. Ensure portability

The source file should work when dragged or copied into a project.

  • Must be a self-contained TSX file
  • Assets must be remote with stable public URLs, no local ones
  • Non-websafe fonts must be loaded through Google Fonts
  • No highly branded videos

4. Define size

To give an Element a fixed size, set elementWidth and elementHeight on the ElementPage in index.mdx:

tsx
<ElementPage
  component={MyElement}
  elementWidth={900}
  elementHeight={260}
>

Then make the Element fill that area in its source file:

tsx
import {AbsoluteFill} from 'remotion';

export const MyElement = () => {
  return <AbsoluteFill>...</AbsoluteFill>;
};

Size the Element to the smallest useful bounding box.

Use previewPadding on the ElementPage if the docs preview needs more breathing room without changing the Element's drag dimensions or adding source padding.

Do not put the wrapper <Sequence> into the Element source file.

5. Separate placement from animation

Let the Studio-inserted <Sequence> handle placement in the video. Don't use left, right, top, and bottom, etc. to position your element.

Inner Element styles can animate entrance values such as translate, scale, and opacity.

For animation, keep the timing inside the component but let the surrounding project decide how long the Element appears:

tsx
import {Interactive, interpolate, useCurrentFrame} from 'remotion';

export const AnimatedElement = () => {
  const frame = useCurrentFrame();

  return (
    <Interactive.Div
      name="Container"
      style={{
        width: '100%',
        height: '100%',
        opacity: interpolate(frame, [0, 20], [0, 1]),
        translate: interpolate(frame, [0, 20], ['0px 40px', '0px 0px']),
      }}
    >
      Hello
    </Interactive.Div>
  );
};

6. Add interactivity where useful

Only add Interactive.* wrappers when they expose useful Studio controls. Every Interactive.* wrapper should have a descriptive name.

Avoid:

  • Redundant nested Interactive.* wrappers that represent the same visual object
  • Repeating the Element display name inside source names; the dragged wrapper already gets named from displayName

For the main inner editable element, use a generic name such as Container.

You should follow Interactivity best practices.

For a single editable object, use one named wrapper:

tsx
import {Interactive, interpolate, useCurrentFrame} from 'remotion';

export const Badge = () => {
  const frame = useCurrentFrame();

  return (
    <Interactive.Div
      name="Container"
      style={{
        opacity: interpolate(frame, [0, 20], [0, 1]),
        translate: interpolate(frame, [0, 20], ['0px 24px', '0px 0px']),
        padding: 24,
        borderRadius: 16,
        backgroundColor: 'white',
      }}
    >
      Launch day
    </Interactive.Div>
  );
};

If a child has separate useful controls, make the child interactive and name it:

tsx
import {Interactive} from 'remotion';

export const Badge = () => {
  return (
    <Interactive.Div
      name="Container"
      style={{
        padding: 24,
        borderRadius: 16,
        backgroundColor: 'white',
      }}
    >
      <Interactive.Div
        name="Title"
        style={{
          color: 'black',
          fontSize: 44,
          fontWeight: 700,
        }}
      >
        Launch day
      </Interactive.Div>
    </Interactive.Div>
  );
};

7. Open the pull request

When submitting an Element as a pull request, include:

  • The Element folder
  • What makes it useful
  • A rendered preview, if available
  • Links to inspiration, if any

Open a pull request on GitHub