Skip to content

Framer

Lupian ships a production-tested Framer code component (RiveFramerEmbed) — drop it in, paste the .riv URL, done.

Use this when

You are integrating a Lupian .riv file into a Framer site.

What you need

  • The .riv file hosted at a public URL (Framer cannot read files from disk).
  • The state machine name from handoff.md.
  • Framer's code component capability (any paid plan).

The fastest path

Use the prebuilt Rive Framer Embed component shipped with this repo. See Rive Framer Embed for the full source and property controls.

If you would rather drop a minimal component into Framer directly, paste this:

tsx
import { useEffect, useRef } from "react";
import { addPropertyControls, ControlType, RenderTarget } from "framer";

export default function RiveAnimation(props: {
  src: string;
  stateMachine: string;
}) {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  // Framer's design canvas has no WebGL — show a placeholder there.
  if (RenderTarget.current() === RenderTarget.canvas) {
    return (
      <div style={{
        width: "100%", height: "100%",
        background: "#1a1a2e", color: "#fff",
        display: "grid", placeItems: "center",
      }}>
        Rive Animation
      </div>
    );
  }

  useEffect(() => {
    let r: any;
    (async () => {
      // -single bundles WASM inline — avoids Framer's stale-WASM cache issue.
      const rive = await import("@rive-app/webgl2-single");
      if (!canvasRef.current) return;

      r = new rive.Rive({
        src: props.src,
        canvas: canvasRef.current,
        autoplay: true,
        stateMachines: props.stateMachine,
        useOffscreenRenderer: true,
        onLoad: () => r.resizeDrawingSurfaceToCanvas(),
      });
    })();
    return () => r?.cleanup();
  }, [props.src, props.stateMachine]);

  return (
    <canvas
      ref={canvasRef}
      style={{
        width: "100%", height: "100%",
        touchAction: "none", pointerEvents: "auto",
      }}
    />
  );
}

addPropertyControls(RiveAnimation, {
  src:          { type: ControlType.String, title: "Rive URL" },
  stateMachine: { type: ControlType.String, title: "State Machine",
                  defaultValue: "State Machine 1" },
});

Why -single in Framer

Framer resolves npm packages via the JSPM CDN, which can cache an old WASM binary across versions. Symptoms: meshes missing, text not rendering, blend modes look wrong. Switching to @rive-app/webgl2-single (WASM inlined in the JS) avoids the separate WASM fetch entirely.

Common mistakes

  • Using a local .riv file path. Framer cannot load files from disk — host the .riv somewhere reachable.
  • Forgetting the design-canvas placeholder. RenderTarget.canvas has no WebGL or audio — your component will throw in the editor without the placeholder branch.
  • Pointer events not reaching the canvas. A Framer layer above the canvas is intercepting them. Set pointerEvents: "auto" and touchAction: "none" on the <canvas>, and make sure no overlay sits on top.
  • Canvas mounts at 0×0. Make sure the Framer frame has explicit width and height — the component fills its parent.
  • Stale WASM after a Framer redeploy. Use @rive-app/webgl2-single, or hard-reload after a Framer publish.

If stuck, send this to Lupian

  • The Framer project link (with permission to view).
  • The .riv URL you pasted into the property control.
  • A screenshot of the property controls and the live preview.
  • See Ask Lupian for help.

Developer support for Lupian-delivered Rive animations