FNJ

How Framer's Appear Animations Actually Work Under the Hood

A real look at the framer/appear script format, the opacity: 0.001 convention, and why this is the one interaction that survives a static export intact.

PublishedJuly 17, 2026
Read time2 min
Written byThe Framer → Next.js team
All posts ↗

Most explanations of Framer's animation system stay at the surface level — "scroll reveals fade elements in." Here's what's actually happening in the markup, because it explains both why appear animations are recoverable and why almost nothing else in Framer's interaction system is.

The data structure

Every server-rendered Framer page that uses scroll/appear animations includes one or more <script type="framer/appear"> blocks. Inside is JSON, keyed by a per-element appear ID, that looks roughly like this shape: an initial state, an animate (target) state, and a transition spec. The transition can be a duration/easing pair, or full spring parameters — mass, stiffness, damping (expressed as "bounce" in some versions), and delay. This is the actual, authored animation data from whoever designed the site — not a runtime computation, not an approximation.

Why opacity starts at 0.001, not 0

This one trips people up. Framer deliberately initializes hidden elements to opacity: 0.001 instead of 0. It's not a bug — it's a convention that keeps the element technically "visible" to certain browser heuristics (layout, some accessibility and rendering paths) while being visually imperceptible, avoiding edge cases that a hard 0 can trigger in some engines. If you ever see this value in a Framer page's inline styles, that's the signal: this element has an appear animation and starts hidden.

Why this is recoverable and hover/tap animations aren't

The critical difference: appear animation data is serialized into the page's HTML before any JavaScript runs, because Framer needs the correct initial state to exist for the very first paint (otherwise you'd see a flash of the wrong state). Hover and tap animations have no equivalent requirement — nothing needs to know the hover state before you've actually hovered, so Framer never serializes it anywhere. It only exists as logic inside the runtime bundle, computed live.

This is the whole reason a Framer-to-static-HTML converter can faithfully rebuild scroll-reveal animations (the data is right there in the source) but generally can't faithfully rebuild hover states (the data was never written down anywhere outside the runtime) without a separate tool that actually drives the live site and records real interaction deltas.

What a correct reconstruction looks like

Parse every framer/appear script from the raw page response (not a second, separate fetch — the appear-ID hashes need to match the exact document they came from). Map initial/animate/transition per element. Rebuild with a real animation library — Framer Motion is the natural match since it's built by the same underlying team and shares the same spring-physics model. Trigger on scroll-into-view with an IntersectionObserver matching Framer's own once semantics.

Done correctly, this is one of the only Framer interactions a runtime-free export can reproduce with genuine, authored-value fidelity rather than an approximation.

#framer#technical#animations#appear

Convert your Framer site next

Free, takes about a minute, and you can preview the result before deploying.

Convert free →
Up nextThe Best Framer to Next.js Converter Options in 2026, Compared Honestly

Ready to convert your website?

Paste your published Framer URL to generate a production-ready project in minutes.

How Framer's Appear Animations Actually Work Under the Hood — Framer → Next.js Optimizer