RemotionUI

Import from After Effects

remotion-ui import-ae converts a Bodymovin / Lottie JSON export into editable Remotion TSX built on ae-motion: named layer components, keyframes as data, and a composition with the right size, fps and length.

@remotion/lottie plays a Lottie file back. import-ae turns it into source: React components you can read, edit, parametrize and retime, built on the ae-motion primitives. Keyframes become ae-motion tracks with AE's own speed/influence easing, layers become named components, and nothing runs lottie-web at render time.

npx remotion-ui@latest import-ae hero.json

Inside a RemotionUI project this writes src/compositions/hero/index.tsx (plus one file per precomp), registers the composition in Root.tsx with the export's width, height, fps and duration, and installs the ae-import runtime (which pulls in ae-motion, bezier-path and shape-ops).

FlagEffect
-o, --out <dir>Output directory. Default: <compositions alias>/<name>, or ./<name> outside a project.
-n, --name <Name>Component and composition id (PascalCase). Default: the export's name, else the file name.
--skip-unsupportedGenerate even when the file uses unsupported features. They are listed at the top of the generated file and printed.
-f, --forceOverwrite files from an earlier import.
--no-registerLeave Root.tsx alone.
--no-installDo not install the ae-import runtime.
--jsonMachine-readable result, including every finding with its JSON path.

What you get

From the lottie-docs bouncing ball, lightly abridged:

/** "Layer" — shape layer, frames 0–120. */
const layerLayer = shapeLayer({
  name: "Layer",
  inPoint: 0,
  outPoint: 120,
  contents: [
    group(
      "Ellipse Group",
      [ellipse({ position: [204, 169], size: [153, 153] }), fill({ color: "#b53046" })],
      {
        anchor: [204, 169],
        position: [
          { frame: 0, value: [235, 106], easeOut: { speed: 0, influence: 0.333 } },
          { frame: 60, value: [265, 441], easeIn: { speed: 0, influence: 0.001 } },
          { frame: 120, value: [235, 106], easeIn: { speed: 0, influence: 0.333 } },
        ],
      },
    ),
  ],
});

export const Layer = () => <AeLayer layer={layerLayer} />;

export const BouncyBall = () => (
  <AeComposition width={512} height={512} fps={60}>
    <Layer />
  </AeComposition>
);
  • Keyframes are data. Frames are composition frames; speed is units per second and influence 0–1, exactly what AE's Keyframe Velocity dialog shows. Scale is a factor (1 = 100%) and opacity 0–1. Change a key, re-render.
  • Layers are components, listed bottom of the AE timeline first, so later children draw on top. Reorder, wrap or delete them like any JSX.
  • Parenting is a reference: parent: rigLayer. Null layers are plain data.
  • Every value is Animatable: swap a track for a number or an expression ({ frame, time, fps }) => value to parametrize it from props.

Supported

AreaImported
LayersShape, solid, null, precomp (clipped to its size, as lottie-web does)
TimingIn/out points, precomp start time, time stretch, time remap, the export's first frame (ip)
TransformAnchor, position (spatial bézier paths, separate dimensions), scale, rotation, opacity, skew, parenting
ShapesGroup, path (incl. animated paths), rectangle, ellipse, star, polygon
StylesFill, stroke (caps, joins, miter limit, dashes), gradient fill and stroke (linear, radial with highlight, opacity stops)
ModifiersTrim Paths (simultaneous and individual, offset, wrap-around), Repeater, Offset Paths, Zig Zag, Pucker & Bloat
EasingBézier (exact speed/influence mapping), linear, hold, per-dimension curves (split into separate tracks)

Stacking follows AE and lottie-web: a style paints every path above it in its group, nested groups included; a modifier reshapes every path above it, and every style drawing that path sees the result; a repeater copies everything above it, styles included.

Not supported — fails loudly

By default import-ae refuses to write anything when the file uses a feature it cannot carry over, and prints each one with the layer, group and JSON path. --skip-unsupported generates the rest and lists what was left out in the generated file's header.

  • Text layers, image / video / footage layers, cameras, lights, audio (an unsupported layer is kept as a null so layers parented to it still move)
  • Masks, track mattes (the matte source is kept but hidden), layer effects, blend modes
  • Expressions (the pre-expression value is used)
  • 3D layers and auto-orient
  • Round Corners, Merge Paths, Twist
  • Wiggle Paths: Bodymovin does not export it, so there is nothing to import. Add shape-ops' wiggle operator by hand.

Hidden layers and hidden items import with hidden: true, so you can switch them back on.

Fidelity

Measured by rendering each fixture with @remotion/lottie and with the generated TSX at the same frames (first, a keyframe, both quarters, middle, last), then scoring PSNR, SSIM and the share of pixels differing by more than 32/255. The bench, 26 fixtures from Airbnb's lottie-android tests (Apache-2.0) and the lottie-docs examples (CC BY 4.0) plus animated variants of them, lives in apps/web/showcase/import-ae-bench.

  • 25 of 26 fixtures match lottie-web at ≥ 41.9 dB minimum PSNR and ≤ 0.17% mismatched pixels on every sampled frame, including Airbnb's Lottie logo animations and stacked, animated repeaters. The remaining difference is anti-aliasing along edges.
  • An animated Zig Zag fixture scores 27.7 dB on frame 0 only, where the reference is wrong: lottie-web keeps a stale zig-zag when its size is 0. Its other frames score ≥ 58.7 dB.
  • The reference is always lottie-web after it has drawn another frame, never swapped per fixture. The bench README documents the two lottie-web bugs it hit, with evidence.

lottie-web is the reference

lottie-web is what designers preview in (LottieFiles, the Bodymovin preview), so import-ae reproduces its behaviour, quirks included:

  • Repeaters keep their originals. When a repeater's fill or stroke sits below it, those styles also paint the original, un-repeated shapes, so a non-zero offset shows the original alongside the copies.
  • Repeater position and rotation compose as lottie-web does: each copy is rotated and scaled about the anchor, then moved by its position steps.
  • Spatial speed is measured along the motion path, as AE's speed graph measures it, so a position that loops back to its start keeps its ease.
  • Time remap landing exactly on a precomp layer's out point shows the frame before.

Known differences

  • Offset Paths uses a port of lottie-web's own offsetter (per-segment bézier offset, intersection pruning, its round-join arcs) rather than shape-ops' polyline offsetter, so insets and joins match the preview.
  • Colours floor to 0–255 and gradient offsets round to whole percents, both as lottie-web does.

On this page