AEO on Framer: the whole build
Framer does most of an AEO build for you. Every page is pre-rendered to HTML on Framer's servers, the sitemap and robots.txt generate themselves, canonical tags are automatic, and any page returns clean Markdown on request. What's left for you is JSON-LD and semantic structure, both of which need custom code. The traps are all in the CMS.
Category
Platform
Reading time
6 min

Framer does most of an AEO build for you. Every page is pre-rendered to HTML on Framer's servers, the sitemap and robots.txt generate themselves, canonical tags are automatic, and any page returns clean Markdown on request. What's left for you is JSON-LD and semantic structure, both of which need custom code. The traps are all in the CMS.
Desses is a Framer Partner and a Framer Pro Expert, which comes from earning $500 in handoff commissions rather than from anyone at Framer deciding I'm good. We sell Framer development from $8,000, so read the rest knowing where my money comes from.
What Framer hands you before you touch a setting
Open a terminal and ask for a page the way OpenAI's search crawler would.
Against a Framer content page that returns two JSON-LD blocks, one H1, the canonical link and roughly 160,000 characters of body text in the initial HTML response. No JavaScript ran. Nothing hydrated.
Framer's documentation says why: "Although Framer sites are built with React, every page is pre-rendered to HTML on our servers before it is served. AI agents and crawlers that do not execute JavaScript still receive the full text of your page."
Pre-rendering is the whole ballgame, because no major AI crawler runs JavaScript on a live fetch. The "Framer is bad for SEO because it's React" line can go in the bin, and the curl output settles it in ten seconds.
The rest ships free with no configuration: sitemap.xml and robots.txt, canonical tags, per-page meta from CMS variables, a noindex toggle, per-layer data-nosnippet, tables in rich text, and nested routes with category and author pages from reference fields. Markdown delivery needs no configuration either, and it's big enough to have its own article, which also covers the head comment that ships on framer.com and not on your build.
The JSON-LD block, and how the CMS fills it
JSON-LD is a block of structured data in a page's head telling a parser what the page is: type, headline, author, dates and image.
Framer ships no schema type natively. Article, Organization, BreadcrumbList, all of it goes in through Custom Code in Project Settings, scoped to specific pages including CMS pages. The scoping is the part people miss: attach a block to one CMS template and every item in the collection gets its own filled-in version.
Here's the block we ship on an article template:
Three things in that block earn their place.
The | json filter escapes the value safely and it goes on every single field. A curly apostrophe, a quotation mark, a line break pasted out of Google Docs. Any one breaks the block, and a broken JSON-LD block fails silently. The page ships, the client is happy, and the structured data has been invalid for four months by the time anyone runs a validator.
{{Created}} and {{Updated}} are built into every CMS item, so nobody hand-maintains a date field and the schema dates can't drift from the CMS dates. {{Author_Name | json}} pulls through a reference field: change the name in one place and every article that writer published updates.
Framer also ships {{variable | unsafeRaw}}, and its own warning says malformed content can break your site or introduce security risks. A CMS field is user input, and the user is a client's marketing hire eighteen months from now who has never heard of JSON escaping. Escaped by default, always, written into the handover doc.
Native, custom code, or not possible
What you want | Where it comes from | Notes |
|---|---|---|
Pre-rendered HTML for non-JS crawlers | Native | Server-side, every page, no setting |
Markdown version of any page | Native |
|
| Native | Auto-generated and maintained by Framer |
| Native | Auto-generated, overridable by upload on Pro |
Canonical tag | Native | Automatic, with manual override |
Title, meta description, OG image per CMS item | Native | Via CMS variables on the template |
Per-page | Native | Toggle |
| Native | Set per layer |
Redirects with wildcards and capture groups | Native, Pro+ | Not on Basic |
Static file hosting including | Native, Pro+ | This is where an llms.txt upload would live |
CMS pagination | Native | Infinite scroll or Load More, client-side past batch one |
Nested routes like | Native | Plus category and author pages via reference fields |
Tables in rich text | Native | Since March 2025 |
Programmatic CMS writes and publishing | Native, beta | Server API, JavaScript SDK over a persistent WebSocket |
Article, Organization, BreadcrumbList, any schema | Custom code | Custom Code in Project Settings, scoped per page |
Breadcrumbs themselves | Custom code | The visible component as well as the markup |
Semantic tags ( | Custom setting | Default to |
A REST endpoint for the CMS | Build it yourself | Framer's SDK is WebSocket; a REST wrapper is on you |
RSS feed | Not possible | No workaround |
Numbered pagination URLs like | Not possible | Pagination has no URL of its own |
The traps are in the CMS
Heading tags in Framer are assigned per text layer, and nothing stops you putting an H4 directly under an H2. No warning, no validator. On a page built by three people over two months, the outline turns into a mess that no human looks at and every parser does. The fix is structural: lock heading tags to text styles, one style per level. Then walk the outline before publish.
Semantic tags default to div until you say otherwise, per frame in the Accessibility panel. A page of nested divs reads fine as text and hands a parser no structure at all.
Now the one that actually costs money. Changing a CMS slug doesn't create a redirect. Framer's redirects article puts it plainly: a sub-path changed from the canvas or the CMS won't update your redirect settings on its own. The old URL just stops existing, and every link into it goes with it. Slugs are cheap to change. That's the problem.
This belongs in the handover doc as a written rule. Ours reads: slugs are final at publish, and if one has to change, the person changing it creates the manual redirect in the same session. I've seen a client rename six slugs in an afternoon to make them "more consistent" and take out a year of accumulated links. Nobody noticed for five weeks.
Which plan you actually need
Basic gives you 2 CMS collections and 1,000 items, with no site redirects. An Articles plus Categories plus Authors hub is three collections. That single fact decides the plan before any AEO consideration enters it.
Pro starts at 10 collections, then $40 per additional 10 up to 40. Items start at 2,500, then $20 per 10,000 up to 40,000. Pro also carries redirects, staging and full static file hosting, which makes it the plan that fixes the slug trap.
Pro is the floor.
The test to run before you launch
Two curls against a real published page.
A pass on the first is a character count in the tens or hundreds of thousands, your H1 present, the canonical link there, and your JSON-LD block with real values filled in. If {{Title}} is still sitting in the output as literal text, the custom code isn't scoped to that CMS template and the block is decorating nothing.
A pass on the second is YAML frontmatter, then your body as Markdown, then the ## Navigation section Framer appends. Nothing back at all means the page hasn't been optimised yet, or the site is rate-limited.
Run both against every template. A template that fails takes every item under it down with it, and one that passes covers the whole collection. While you're there, run the site through Framer's AEO scanner.
When Framer is the wrong platform for this
Two hard ceilings. Forty CMS collections, forty thousand items, fully paid and fully loaded, with no plan above them. A publisher with a decade of archive or a documentation site with per-version content will hit one.
Large editorial operations with roles, approvals and scheduled multi-locale publishing are not a Framer job either. Saying that costs me work, and it's the reason the rest of this article is worth reading. If that describes you, Framer against Webflow and Next.js covers the ceilings in detail.
What we do on a build, in order
Text styles first, with heading tags locked to them before a page gets laid out. Retrofitting heading structure onto a finished site is miserable work. Semantic tags set per frame as each template gets built, because a cleanup pass at the end always misses two.
CMS structure next: Articles, Categories, Authors, with the reference fields wired so author and category pages generate themselves. Then the JSON-LD block in Custom Code, scoped per template, every field through | json. Then the answer to the page's question in visible text near the top, one topic per URL. Then the two curls, on every template.
Then the handover doc, carrying the slug rule, the heading-style rule and the redirect procedure. A build that only works while I'm the one touching it isn't finished. That sequence is what our Framer development work covers, from design through handover.
If you're on Framer and want to know what a crawler sees, send me your URL. I'll run both curls against your three most important pages, walk the heading outline, and send back what came out plus what I'd change. Twenty minutes of my time, costs nothing, and if it comes back clean I'll tell you that and we're done.













