On this page10 sections
The problems that come up most when moving a Framer site to Next.js fall into eight groups: layout, missing assets, animations, CMS data, forms, URLs, metadata and build failures. This page lists each problem with its symptoms, cause and fix, and says whether Deframer handles it automatically. Every entry is either reproduced in our October 2026 test runs or backed by a named test in the converter's code. Deframer also exports the same conversion to TanStack Start, Astro, React Router and Vite; most entries apply to all five, and fixes that name Next.js files are for the Next.js export.
It is a maintained list: we add entries when a test run reproduces something new and mark them fixed when a later run confirms it.
How to read this database
Each entry has the same fields:
- Symptoms: what you see in the browser, the terminal or search tools.
- Cause: why it happens.
- Fix: what to do in the Next.js project.
- Deframer: Yes (handled automatically, with the mechanism), Partly, or No (reproduced, needs a manual fix).
- Evidence: Reproduced (seen in our run of 25 October 2026, converter commit 692fe4c) or Code (a test or document in the converter's repository shows the mechanism).
The test sites are described in real conversion tests on five websites and the benchmark; the measuring method is in our testing methodology. For general troubleshooting of the conversion flow itself, see common Framer to Next.js conversion issues.
Category | Entries | Seen in this run | Handled automatically | Partly | Open |
|---|---|---|---|---|---|
Layout | 3 | 1 | 3 | 0 | 0 |
Missing assets | 4 | 2 | 2 | 1 | 1 |
Animations | 5 | 2 | 3 | 0 | 2 |
CMS data | 4 | 3 | 2 | 1 | 1 |
Forms | 3 | 0 | 3 | 0 | 0 |
URLs and routing | 4 | 3 | 4 | 0 | 0 |
Metadata | 4 | 3 | 3 | 0 | 1 |
Build and type errors | 6 | 4 | 3 | 1 | 2 |
"Seen in this run" includes problems we checked for and found handled correctly, such as unknown URLs returning 404.
Layout
Layout problems show up as elements in the wrong place, the wrong size, or the wrong breakpoint. Framer to Next.js layout issues goes deeper on diagnosing them.
The wrong breakpoint shows at some widths
- Symptoms: At a tablet or phone width the page shows the desktop (or phone) layout; the page height differs from the live page.
- Cause: Framer renders every breakpoint's layout and hides the inactive ones with media queries. A hand port that uses different breakpoints (Tailwind's defaults, for example) switches layouts at the wrong widths.
- Fix: Keep the site's own breakpoints. Read them from the page's breakpoint map and use the same media queries, as described in Framer breakpoints and responsive layouts in Next.js.
- Deframer: Yes. Breakpoints are kept per page with the site's own media queries. In our run, page heights matched the live page on 122 of 126 captures at 1440, 810 and 390 px on the five detailed sites (the exceptions: one CMS page with a separate defect, at three widths, and one capture where the live page itself changed height between captures), and on all 56 benchmark captures.
- Evidence: Reproduced (no breakpoint mismatches found).
Text that "fits" its box wraps or overflows
- Symptoms: A headline set to fit its container is too large or too small, or wraps onto an extra line.
- Cause: Framer's fitted-text measures text in the browser and scales it. Copied static CSS loses that.
- Fix: Measure and scale on the client, after fonts load.
- Deframer: Yes. The kit reimplements fitted text; a browser regression test covers it (
test/browser/fit-text.test.ts, run withnpm run test:browser). - Evidence: Code.
Content shifts slightly at one width only
- Symptoms: A section is offset by a few pixels at one width; a pixel comparison shows a band of mismatch.
- Cause: Usually a font that loaded with a different
font-displayor subset than on the live site, which changes line breaks. - Fix: Serve the same font files and subsets, with the same
font-display. - Deframer: Yes. Font faces are harvested from the published page with their subsets and
font-display(tests intest/fonts.test.ts: "a face keeps the font-display the published page gives its file"). - Evidence: Code. In our run, the pages we measured with mismatches between 1% and 5% did not show height changes; we did not establish a single cause for each.
Missing assets
Images and videos still load from Framer's CDN
- Symptoms: The network panel shows
framerusercontent.comrequests after migration. - Cause: That's the default: assets stay on Framer's CDN unless you choose otherwise.
- Fix: Choose "Assets in the project" when you convert, or download the files yourself into
public/. See Framer fonts and assets: CDN vs local for the trade-offs. - Deframer: Yes, as an option. With "Assets in the project", every Framer-hosted image (and srcset variant), font, video and file the project references is downloaded and referenced by path; a file that fails to download stays remote and is listed.
- Evidence: Code (converter README, "Assets: CDN or local"; our runs used CDN mode).
A video or embed hosted elsewhere doesn't load
- Symptoms: A blank area where a video plays on the live site; the console shows
net::ERR_ABORTEDfor a third-party host. - Cause: Assets on hosts other than Framer's (a code component's own video host, for example) are not Framer assets, and they can fail or be blocked on any domain.
- Fix: Move the file to your own storage and update the URL in the component.
- Deframer: Partly. Such URLs are kept as they are; they are not downloaded.
- Evidence: Reproduced. Site 2 (agency) loaded a hero video from a third-party host; requests to it were aborted on both the live and converted pages during our captures, so the comparison wasn't affected.
A blocked request for a "__SystemDefault" font
- Symptoms: The network panel shows a blocked request (
ERR_BLOCKED_BY_ORB) tofonts.googleapis.com/css2?family=__SystemDefault…. - Cause: A request for Framer's system-font placeholder, which is not a real web font.
- Fix: Harmless; text uses the system font either way. You can ignore it.
- Deframer: No (cosmetic; reproduced, open as of 25 October 2026).
- Evidence: Reproduced on site 3; no visible effect in the captures.
Social images are relative URLs after moving assets local
- Symptoms:
og:imageis a path like/images/social-image.png; Next.js warns at build time; link previews show no image. - Cause: Open Graph images must be absolute URLs, and Next.js resolves relative ones against
metadataBase. - Fix: Set
metadataBaseinsrc/app/layout.tsxto your production domain. - Deframer: Yes. The root layout sets
metadataBaseto the site's published URL; change it when the domain changes. - Evidence: Code (README, "Assets"; what you get in a converted project).
Animations
A page throws "MotionCancelOptimisedAnimation is not a function"
- Symptoms: The console shows
TypeError: … MotionCancelOptimisedAnimation is not a function; part of the page stays in its pre-animation state (hidden or offset). - Cause: Framer starts entrance ("appear") animations from an inline script before React loads, and hands them over to the animation library afterwards. In the converted project the kit's appear script removes its cancel function once all its animations are handed over, while a value listener installed earlier can still call it.
- Fix: In
src/lib/deframer/effects/appear-script.ts, call the function only if it still exists (w.MotionCancelOptimisedAnimation?.(id, name)) inside the listener inMotionCheckAppearSync, then rebuild and check the page. - Deframer: No (reproduced, open as of 25 October 2026).
- Evidence: Reproduced on two of twelve sites: one page of site 2 (33 to 38% pixel mismatch at all three widths) and the home page of one benchmark site (5.3% at 1440 px).
Sections sometimes haven't appeared when the page is captured
- Symptoms: Occasionally a text block or image gallery on a converted page is still hidden (or images are grey placeholders) when you'd expect it to be visible; reloading shows it correctly.
- Cause: Not established. It was intermittent: the same page measured under 1% at other widths and in other runs. Timing of in-view entrance effects and lazy images is the likely area.
- Fix: Scroll through the page on a phone and a desktop after deploying; if a section stays hidden, check its entrance effect and the console.
- Deframer: Under investigation.
- Evidence: Reproduced on site 3 (animated portfolio), one project page at 1440 px in two separate runs (about 25% mismatch), under 1% at 810 and 390 px.
Springs and easings feel different
- Symptoms: Hover and entrance animations run at a different speed or with a different bounce.
- Cause: Re-creating animations by hand with a generic duration and easing.
- Fix: Keep Framer's transition values. Springs need their stiffness, damping and mass.
- Deframer: Yes. Transitions are written with the original values on
motion/react, and entrance animations play the library's own spring (test/appear-script.test.ts: "a spring plays as the library's own spring: its duration and a linear() easing sampled from it"). See keeping Framer animations in Next.js with Motion. - Evidence: Code. With motion allowed, 23 of 26 captures of the first routes of each detailed site measured under 1% mismatch (the others: 1.5%, 1.5% and 4.5%). Screenshots show end states, not timing.
Code imports framer-motion and versions clash
- Symptoms: Two copies of the animation library, or import errors after upgrading.
- Cause: Framer code and marketplace components import
framer-motionby various URLs and subpaths. - Fix: Point every import at one package.
- Deframer: Yes. All
framer-motionimports become the matchingmotionentry (test/motion-package.test.ts). - Evidence: Code.
Reduced-motion visitors see a different page
- Symptoms: With "reduce motion" on, elements are missing or stuck mid-animation.
- Cause: An entrance animation that never runs leaves its element at its starting opacity or position.
- Fix: Jump transforms to their end state and keep fades when the site follows the visitor's setting.
- Deframer: Yes (
test/appear-script.test.ts: "reduced motion … jumps transforms and keeps fades"; the site's reduced-motion setting is kept insrc/site/routes.ts). - Evidence: Code. Our visual comparison runs with reduced motion on by default, and the 12-site results above include those pages.
CMS data
CMS item pages with special characters in the slug answer 404
- Symptoms: Most blog posts work, but a few return the 404 page. Their slugs contain a non-ASCII character, such as a curly apostrophe (
tomorrow’s). - Cause: The page is prerendered under its decoded path, but the request for the percent-encoded URL (
tomorrow%E2%80%99s) doesn't match it in the converted project. - Fix: Until fixed, change those slugs to plain ASCII in
src/cms/<collection>.jsonand add a redirect from the old URL innext.config.ts, or removedynamicParams = falsefrom the route and look items up by the decoded slug. - Deframer: No (reproduced, open as of 25 October 2026). The converter has tests for decoded parameters and literal percent sequences in CMS routes (
test/cms-route-guard.test.ts), but this case still failed. - Evidence: Reproduced on site 4 (CMS blog): 3 of 86 sitemap URLs answered 404 locally and 200 on the live site.
A collection stays on Framer's data format
- Symptoms: The conversion lists a notice like "CMS src/cms/notes.tsx kept on Framer's reader"; there is no
notes.jsonto edit. - Cause: The collection is published in a form the converter could not decode into JSON records.
- Fix: The pages still build and render. To edit the content in code, export the collection as CSV from Framer and convert it to the JSON shape the other collections use, or move it to a headless CMS.
- Deframer: Partly. Pages render and every item is prerendered, but the data isn't plain JSON.
- Evidence: Reproduced on site 3 (portfolio): one collection, all item pages served.
Unknown CMS slugs render an empty page instead of 404
- Symptoms:
/blog/anythingshows a blank template with status 200. - Cause: A dynamic route that accepts any parameter.
- Fix: List all items with
generateStaticParams()and setdynamicParams = false. - Deframer: Yes (
test/cms-route-guard.test.ts: "single-locale CMS routes reject unknown items before metadata and render"). See Framer CMS to JSON in Next.js. - Evidence: Code, and reproduced as correct: on all five detailed sites an unknown URL answered 404, as on the live site.
Rich text loses links or embedded components
- Symptoms: Links inside CMS rich text point at Framer page ids; embedded components are missing.
- Cause: Framer stores rich text in its own format.
- Fix: Convert to a readable tree with links as paths and components by name.
- Deframer: Yes (
test/cms-rich-text.test.ts: "links becomeanodes with paths", "embedded components are named"). - Evidence: Code.
Forms
None of the five detailed sites had Framer native forms, so these entries rest on the converter's tests. See forms after leaving Framer.
Forms still post to Framer
- Symptoms: Submissions go to
api.framer.comand stop arriving once the Framer site is unpublished. - Cause: Copying the form markup keeps Framer's form
action. - Fix: Give each form its own route handler and deliver submissions yourself.
- Deframer: Yes. Each native form gets
src/app/api/forms/<name>/route.ts(test/forms.test.ts: "native forms point at their route; fields, redirect and name come from the JSX"). - Evidence: Code.
Spam and validation
- Symptoms: Bot submissions, or empty submissions accepted.
- Fix: Validate fields server-side, add a honeypot and a rate limit.
- Deframer: Yes (
test/forms.test.ts: "invalid fields are refused with a reason per field", "a filled honeypot is answered like a success and never delivered", "one address gets five submissions a minute"). - Evidence: Code.
Submissions don't arrive anywhere
- Symptoms: The form says "thanks" but nobody gets the message.
- Cause: Delivery isn't configured; by default submissions are only logged.
- Fix: Set
FORMS_WEBHOOK_URL(or the form's own variable from.env.example) to Zapier, Make, Slack or your CRM. - Deframer: Yes, once configured (
test/forms.test.ts: "submissions are forwarded to the site-wide and the form's webhook as JSON", "a failing webhook makes the submission fail"). - Evidence: Code.
URLs and routing
Trailing-slash URLs redirect or 404
- Symptoms:
/about/and/aboutbehave differently from the live site; Search Console reports redirects. - Cause: Next.js applies its own trailing-slash rule; Framer publishes one form per page.
- Fix: Serve each page at its published form and redirect the other with 308.
- Deframer: Yes.
src/proxy.tsdoes this for every site (test/routing.test.ts: "a single-locale site's proxy: each page at its published form, unknown URLs 404 without a redirect"). - Evidence: Code, and reproduced as correct: every sitemap URL on sites 1, 2, 3 and 5 answered as on the live site (200, or the same 308 for
/deto/de/on the multilingual site).
Published redirects are lost
- Symptoms: Old URLs that redirected on Framer now 404.
- Fix: Recreate them in
next.config.tsredirects(). - Deframer: Yes. The site's redirects are read from the live site and written to
next.config.ts(test/routing.test.ts: "redirects keep the published form of their destination"). - Evidence: Code. None of the five detailed sites published redirects.
Pages missing from the converted site
- Symptoms: A URL in the live sitemap has no route.
- Fix: Compare the sitemap with
.next/prerender-manifest.jsonafter building. - Deframer: Yes in this run: every sitemap URL on all five detailed sites had a prerendered route (the only "missing" one was a sitemap entry for the 404 page itself, which is served as the 404 page).
- Evidence: Reproduced (no gaps found).
Extra pages that weren't in the sitemap
- Symptoms: The build lists pages such as
/privacy-policythat the live sitemap doesn't. - Cause: The designer excluded them from the sitemap in Framer; they are still published.
- Fix: Nothing, unless you want them indexed. The converted
public/sitemap.xmlis the published one, so they stay out of it. - Deframer: Yes (sitemap kept as published:
test/routing.test.ts, "sitemaps as Framer writes them"). - Evidence: Reproduced on site 2 (two legal pages).
Metadata
Titles, descriptions or canonical URLs differ
- Symptoms: Search results show a different title; duplicate-content warnings.
- Fix: Set metadata per route, including canonical and
og:url. - Deframer: Yes. Each route's metadata is evaluated at conversion time (
test/routing.test.ts: "a page's metadata carries its canonical URL, og:url and the site's social image"). - Evidence: Reproduced as correct: titles, descriptions and social images matched the live pages on every page we compared (5 on site 2, 19 on site 3, 24 on site 4, 24 on site 5). More in SEO when moving off Framer.
The max-image-preview:large robots tag is missing
- Symptoms: The live page has
<meta name="robots" content="max-image-preview:large">; the converted page doesn't. - Cause: The converted page metadata didn't carry this robots value on most pages of one site.
- Fix: Add
robots: { "max-image-preview": "large" }to the root layout'smetadatainsrc/app/layout.tsx. It lets Google show large image previews; it does not affect indexing. - Deframer: No (reproduced on one site, open as of 25 October 2026).
- Evidence: Reproduced on site 3: missing on 16 of 19 pages compared.
The lang attribute differs
- Symptoms: The converted
<html>haslang="en"where the live page has nolang. - Cause: The converter sets the site's locale on the root element.
- Fix: None needed in most cases; screen readers and search engines use it. Change it in
src/app/layout.tsxif the language is wrong. - Deframer: Yes (deliberate difference).
- Evidence: Reproduced on sites 2 and 3.
Viewport settings change
- Symptoms: Mobile zoom behaves differently.
- Fix: Export Next's
viewportwhere the page's setting isn't the default. - Deframer: Yes (
test/metadata.test.ts: "viewport settings become Next's viewport export, Framer's default none"). - Evidence: Code.
Build and type errors
tsc --noEmit reports errors in code components
- Symptoms:
npx tsc --noEmitlists errors such asType 'string' is not assignable to type 'Easing | Easing[]'insrc/code/*orsrc/vendor/*, whilenpm run buildsucceeds. - Cause: Code components were written in Framer's code editor, which doesn't type-check. The project keeps
typescript: { ignoreBuildErrors: true }innext.config.tswhen it contains them, with a comment explaining why. - Fix: Fix the types file by file (for example
ease: "easeInOut" as const), then removeignoreBuildErrors. Framer code components and overrides in Next.js walks through it. - Deframer: Partly: builds pass; the type errors are the site's own code, left as written.
- Evidence: Reproduced: 8 of 12 benchmark sites had at least one
tscerror; 11 of 12 built without changes (the 12th failed for the reason in the next entry).
Type errors in generated components
- Symptoms:
tscerrors insrc/components/*, such asType 'number' is not assignable to type '"Span All"'. - Cause: A generated component's props typed narrower than the values an instance passes.
- Fix: Widen the prop type in that component.
- Deframer: No (reproduced on one site, 8 errors; the build still passes).
- Evidence: Reproduced on one benchmark site.
next build fails with "Parsing ecmascript source code failed"
- Symptoms: Turbopack stops with "Parsing ecmascript source code failed" and points at a file in
src/code/;tscreports'*/' expected. - Cause: In our run, the converter's pass that removes Framer editor annotations (such as
@framerSupportedLayoutWidth) from code-component comments left a documentation comment unclosed: the line reads/** @supportedLayoutWidth autowith no*/, so the rest of the file is a comment. - Fix: Add
*/at the end of that line in each file the error names, then build again. - Deframer: No (reproduced, open as of 25 October 2026).
- Evidence: Reproduced on site 5 (multilingual): two files, two one-line fixes; the project then built in 4.0 s.
"window is not defined" during build
- Symptoms:
next buildfails while prerendering withReferenceError: window is not defined. - Cause: Code that touches browser APIs when the module loads, which runs on the server during prerendering.
- Fix: Load such modules only in the browser.
- Deframer: Yes. Modules that need the browser are detected and loaded client-only (
test/client-only.test.ts), and the conversion lists them as a notice ("client-only (needs the browser to load or render)"). - Evidence: Reproduced as handled: the notice appeared on sites 2 and 5 and on several benchmark sites, and none of their builds failed on browser APIs (site 5 failed for the separate reason below).
npm packages that require React 18
- Symptoms:
npm installfails with peer-dependency errors against React 19. - Fix: Use the closest package version that accepts React 19.
- Deframer: Yes (
test/react-peers.test.ts: "React-18-only packages move to the closest version accepting React 19"). - Evidence: Code.
Packages without versions or types
- Symptoms: Unpinned packages update and break; missing type declarations.
- Fix: Pin the exact release the site used and add
@types/*where needed. - Deframer: Yes (
test/package-versions.test.ts: "ranges and unpinned packages get the exact release a CDN serves", "packages without their own types get @types at the matching release"). - Evidence: Code. Benchmark projects pinned exact versions (for example a smooth-scroll package at
1.1.9).
FAQ
What is the most common problem when converting Framer to Next.js?
In our October 2026 run, type errors in code components were the most common (8 of 12 sites), but they don't stop the build. The most serious were one build failure caused by an unclosed comment, an animation error on 2 of 12 sites, and CMS slugs with special characters returning 404.
Why does my converted Framer site show a blank section until I scroll?
Usually an entrance animation that never started or finished. Check the console for an error such as "MotionCancelOptimisedAnimation is not a function" and see the animation entries above.
Why do some Framer CMS pages 404 after migration?
Check whether their slugs contain non-ASCII characters such as curly quotes or accented letters. Rename the slugs to ASCII and add redirects, as described in the CMS section.
Is it safe to turn off ignoreBuildErrors?
Only after npx tsc --noEmit reports no errors. Otherwise your build will fail on type errors that Framer never checked.
How often is this list updated?
When a new test run reproduces a problem or confirms a fix. Each entry says whether it comes from a reproduced run or from the converter's tests, with the date of the run.
