Fix AI visibilityFramer

Why AI crawlers can't read your Framer site

A published Framer site is pre-rendered to static HTML and is usually readable without JavaScript, so when ChatGPT misses your content the cause is specific: a code component, an embed, or an interaction that keeps text out of the HTML.

5 min read Lantad

Framer serves your site as pre-rendered static HTML from a CDN, so unlike a plain React single-page app, a Framer page usually reaches ChatGPT's crawlers with its native text already in place. If a scan shows your content missing, the cause is almost always one specific choice, not the platform itself.

This guide covers the ways a Framer page can hand a crawler less than a visitor sees, how to check which one is happening, and the exact change for each. It ends with how to verify the fix with a Lantad scan.

The fix, in short

  • Keep primary text as native content, not a code component
  • Guard browser APIs so code components stay in the server HTML
  • Do not hide important content behind an interaction or embed
  • Set the Framer SEO basics and JSON-LD
  • Verify with a Lantad scan

Usually readable

  • pre-rendered to static HTML
  • native text ships in the source
  • no framework fix needed
  • start by verifying, not rewriting

What still hides text

  • code components using browser APIs
  • content behind an interaction
  • third-party embeds
  • text baked into images
Framer starts in a good place. The failure modes are things added on top.

How Framer renders

When you publish in Framer, each page is pre-rendered to static HTML and served from a CDN, so the headings, body text, and CMS content you placed on the canvas are usually present in the raw HTML a no-JavaScript crawler receives. That is the common case, and it is why most Framer sites read fine. Content goes missing in three situations. First, a code component or custom code override that reads window, document, or navigator during render cannot run on the server, so Framer hides that component during the server render and retries it in the browser after load. Any text inside that component is absent from the served HTML and is built by JavaScript the crawler does not run. This is scoped to the affected component, not the whole page, so your other native text still renders into the HTML. Second, a third-party embed such as a form, a booking widget, or a chat tool loaded by script or iframe is fetched after load, so its content is never in the served HTML. Third, content that only appears after a user action, such as a tab, an accordion, a modal, or a "load more" control that injects items on click, may not be in the initial HTML at all. Appear-on-scroll animations are safe, because the text is already in the HTML and only its opacity changes.

The fix

  1. Keep primary text as native content, not a code component

    Framer pre-renders text layers and CMS fields into the served HTML, so keep your headlines, body copy, and the actual answers a reader wants as native Framer elements or CMS content. Avoid putting meaningful text inside a code component that fetches or builds it at runtime, because that text depends on JavaScript the crawler will not run.

    Sample Illustrative, not a measurement of any real site.

    Code component

    • renders in the browser
    • may be absent server-side
    • invisible if it touches window
    • hard to audit

    Native text layer

    • pre-rendered into the HTML
    • present with no JavaScript
    • readable by every crawler
    • trivially auditable
    Sample illustration of the same paragraph two ways.
  2. Guard browser APIs so code components stay in the server HTML

    When a code component or override reads window, document, or navigator during the render pass, that API does not exist on the server, so Framer hides the component during the server render and remounts it in the browser after load. Any text inside it is then missing from the static HTML. Guard those calls so they run only after mount, for example behind a typeof window !== 'undefined' check or inside a useEffect, so the component renders on the server and its text lands in the served HTML. This affects only the component, not the rest of the page, but if that component holds primary copy the gap can be large.

    • window or document at module scope Throws during pre-render, so the block is dropped from the HTML.
    • localStorage read on first paint Same failure, same result: no text in the source.
    • Guard the access, keep the text Render the copy unconditionally and only touch browser APIs after mount.
    What breaks a component during pre-render, and what to do instead.
  3. Do not hide important content behind an interaction or embed

    Anything a crawler should read has to be in the initial HTML, not injected when someone clicks a tab, opens an accordion, or after a third-party script loads. Move the important text into the page body. If you rely on a Framer accordion or tab for key content, keep the collapsed content in the DOM rather than removing it, and for a critical embedded tool like a form or booking widget, add the key details or a plain-text summary as native Framer text alongside the embed so the substance is in the HTML.

    • Returns HTML Blocking A 4xx or 5xx ends the scan. Nothing downstream is measurable.
    • Crawler allowed Blocking robots.txt and the live server both have to permit the token.
    • Readable without JS Blocking Most AI crawlers do not execute JavaScript.
    • Structured Improves Headings and schema make the content extractable.
    • Cited Not promised The engine decides. No configuration can guarantee it.
    The order a page has to pass. Failing an earlier rung makes the later ones irrelevant.
  4. Set the Framer SEO basics and JSON-LD

    In page settings, fill the title and description, keep a single H1 with headings in order, and add schema.org JSON-LD through custom code (Site Settings or Page Settings, End of head) so it lands in the served HTML rather than being added later by script. Framer adds no structured data on its own, so this is manual. Framer regenerates sitemap.xml on publish, so confirm the page is included and is not set to hidden or excluded from indexing.

    • One h1, then ordered h2s A machine reads the outline before it reads the prose.
    • Semantic containers main, article and section, rather than nested unlabelled divs.
    • A recognised business type in JSON-LD Organization, LocalBusiness, SoftwareApplication and similar.
    • Server-rendered, not injected JSON-LD added by JavaScript is not in the HTML a crawler receives.
    What the structure and schema checks look for. Together they are 25% of the score.
  5. Verify with a Lantad scan

    Scan the published URL with Lantad. It fetches the page once as LantadBot with no JavaScript and once in a full browser, then diffs them. Prose Parity near 100 means the crawler sees what visitors see, and any gap is the exact text missing from the HTML. Access checks robots.txt and the server response per named crawler such as GPTBot, and Structure and Schema flag heading and JSON-LD issues. Re-scan after each change to confirm the gap closed.

    verify

    • curl the URL with no JavaScript raw HTML
    • search that output for a sentence you expect found?
    • if it is missing, a crawler never sees it fix it
    • then scan to get the graded before and after score
    How to check it yourself, before trusting any dashboard.

Written by

Lantad

Common questions

Are Framer sites readable by ChatGPT by default?

Usually yes. Framer pre-renders each published page to static HTML, so native text and CMS content are in the HTML a no-JavaScript crawler receives. The exceptions are specific: a code component hidden on the server because it touched a browser API, third-party embeds loaded by script, and content that only appears after an interaction. Being readable is a prerequisite for being cited, never a guarantee of it.

Why does my Framer page look full in a browser but empty in a scan?

Your browser runs the JavaScript that builds the page, while an AI crawler reads the raw HTML your server returns. Framer pre-renders your native text into that HTML, but a code component that touches window or document during render is hidden on the server and mounted only in the browser, so its text is absent from what the crawler receives. The same happens with content inside a script-loaded embed or revealed only after a tab or accordion. Lantad's Prose Parity check shows exactly how much of the visible text is missing from the HTML.

Does Framer's static rendering mean I do not need to do anything?

Static rendering covers native text and CMS content automatically, which handles most pages. It does not cover text inside a code component that was hidden on the server, content injected by a third-party embed, or sections revealed only on interaction. Those can still be absent from the HTML, so scan the page to confirm what a crawler actually receives rather than assuming.

See what AI reads on your Framer site

Run a free scan: paste a URL and see exactly what AI crawlers can and cannot read, with a graded report and ranked fixes.