Dark green cover for a Lovable RSS feed guide, with a stylised feed icon of concentric arcs and a coral dot beside the headline Ship an RSS Feed Your AI Agents Can Actually Read.
Dark green cover for a Lovable RSS feed guide, with a stylised feed icon of concentric arcs and a coral dot beside the headline Ship an RSS Feed Your AI Agents Can Actually Read.
AI Readiness

How to publish an RSS/Atom/JSON feed and declare it in the head on Lovable

Lovable splits into two templates for this fix: TanStack Start builds the feed live from a server route, React + Vite needs a static file in public/ instead.

AAsif Rahman 26 Sept 2026 6 min read
#Lovable#RSS#JSON Feed#Atom#TanStack Start#content signals

This guide covers E5 · Content — for Lovable.

Table of contents

A sitemap tells a crawler what exists. It does not tell an agent what changed since last week, and that gap is what check E5 grades: whether the site publishes a dated content feed at all. Run AIScan against a Lovable app today and E5 fails for one of two reasons: no feed file exists, or one exists but nothing in <head> points at it. Both are fixable in under half an hour, and which fix applies depends on which of Lovable's two site templates the project runs.

Quick summary

If your Lovable project is on...Feed methodWhere it livesTime
TanStack Start (default since 13 May 2026)A file-based server route returns real XML on every requestSame domain, e.g. /feed.xml20 to 30 minutes
React + Vite (older projects, not migrated)A static file dropped in public/Same domain, frozen at whatever was last published10 minutes, plus a republish every time content changes
Either template, published only to a preview URLNeither helps yetPreview responses carry x-robots-tag: noindex, nofollow, so no crawler reads the feed regardlessConnect a custom domain first

Check first: run npx aiscan-cli yoursite.com, or paste the URL at AIScan. It names E5 directly, alongside the rest of the content dimension: C1 Markdown negotiation, C3 structured HTML, E3 server rendering. The content checks page covers what each one grades.

Why a feed earns its own file

An RSS, Atom or JSON feed is a cheap, dated changelog. Verified on AIScan's own check guidance for E5, the two specs it cites are RFC 4287 for Atom and JSON Feed 1.1; RSS also satisfies the check even though it predates both. A sitemap lists URLs and rarely carries a lastmod value anyone bothers updating. A feed's whole job is dates, which is the one thing a crawler can't infer from a page it hasn't fetched yet.

What the feed needsWhat AIScan checks for itWhat AIScan cannot see
A parseable RSS, Atom or JSON Feed documentThe file exists, validates, and is declared in <head>Whether the dates inside it are true
A publish date per itemPresent, in the format the spec expectsWhether it matches when the content actually went live

That second column is on you. A feed with stale or fabricated dates is worse than no feed, because it hands an agent a false freshness signal instead of an honest absence of one.

Why the fix forks on your template

According to Lovable's own measurement writeup, fifteen published Lovable apps were fetched with both a browser user agent and GPTBot on 1 September 2026. Data fetched from apps on TanStack Start returned full body copy to both. Apps still on the older React + Vite template returned near-empty pages to anything that wasn't a verified crawler.

TemplateRuns code per request?How you ship the feed
TanStack Start (new default)Yes, a real serverA file-based route builds XML from your data on every request
React + Vite (legacy)No, static files onlyA hand-written file in public/, rebuilt and republished manually

According to Lovable's SEO and AI search page, older projects use "on-request pre-rendering on deployed public URLs, served only to verified search and AI crawlers," while unverified requests get "the regular single-page app." In its own words, Lovable's upgrade guide says TanStack Start "renders each page into finished HTML on the server before sending it. Everyone receives the same complete page, every time," and dates the switch to the platform default at 13 May 2026.

The same split decides how you ship a feed. React + Vite has no server to run code on request, so anything at a URL has to already exist as a file. TanStack Start has real server routes, so it can build the feed from your actual data on every request.

Path A: TanStack Start, a server route

Ask Lovable's AI to add a route file at src/routes/feed[.]xml.ts. The brackets around the dot are required; TanStack Router treats [.] as a literal period rather than a route-segment separator, verified on TanStack's own routing docs on 26 September 2026. Export a GET handler that queries whatever holds your posts (your Lovable Cloud / Supabase table) and returns real XML:

export const Route = createFileRoute('/feed.xml')({
  server: {
    handlers: {
      GET: async () => {
        const items = await getPublishedPosts() // your own query
        const xml = buildRss(items) // title, link, pubDate per item
        return new Response(xml, {
          headers: { 'Content-Type': 'application/rss+xml; charset=utf-8' },
        })
      },
    },
  },
})

This runs on the same domain as the app, so /feed.xml is a real path, not a redirect to a different subdomain, and it updates itself: no republish step when you add a post.

Path B: React + Vite, a static file in public/

An older project has no server to run that handler on, so the feed has to be a plain file. Static files placed in Lovable's public/ folder are served from the site root automatically, the same mechanism that serves robots.txt and llms.txt. Ask Lovable to generate public/feed.xml with your current posts written out as Atom or RSS entries, then publish. It works, but it's frozen: every new post means editing the file and republishing, and nothing enforces that you remember. If the rendering gap on E3 and C3 already has you considering the TanStack Start migration, doing it once covers this check too instead of maintaining a file by hand indefinitely.

Declare it in <head>

Either path needs the same tag, wherever your <head> actually lives:

<link rel="alternate" type="application/rss+xml" title="Blog" href="/feed.xml" />

On TanStack Start, that belongs in src/routes/__root.tsx, inside the head function's links array (createRootRoute({ head: () => ({ links: [...] }) })), per TanStack's document head guide. It is not hand-edited into a static HTML file, because there isn't one. On the older Vite template it's a one-time edit to the single index.html shell in the project root, since a plain SPA serves that one file for every route.

Verify

curl -sI https://yoursite.com/feed.xml
# expect: HTTP/1.1 200, content-type: application/rss+xml or application/xml or application/json

curl -s https://yoursite.com/ | grep -o '<link rel="alternate"[^>]*feed[^>]*>'
# expect: the tag above, not empty

curl -s "https://aiscan.site/api/public/v1/scan?url=https://yoursite.com" | grep -o '"E5"[^}]*'

If the scan still fails E5 after a fresh deploy, check the item dates first. A feed with every pubDate set to build time rather than actual publish time is the most common miss on the static path, and it reads as staleness rather than freshness.

Exclusions

A preview-only Lovable URL fails every content check regardless of what you fix, because of the noindex header above; connect a custom domain before troubleshooting anything else. A feed with fewer than about five items reads as thin. Hold off publishing it until there's enough history to make the dates meaningful, and add the file once there is.

Once the feed is live, run the AIScan scan again to confirm E5 alongside the rest of the content dimension, then check C3, title tag, meta description and schema and the full rendering picture. The content dimension guide and more Lovable fix guides cover the rest.

Frequently asked questions

My Lovable app returns a 404 for /feed.xml. What's wrong?

On TanStack Start, confirm the route file is named exactly feed[.]xml.ts with the brackets, not feed.xml.ts — without them the router treats the dot as a path segment and the route never matches. On the older Vite template, confirm public/feed.xml actually exists and was included in the last publish; files added to public/ only take effect after a new deploy.

AIScan still reports E5 as failing after I added the feed. Why?

Re-scan a few minutes after the deploy finishes, not before; Lovable's publish step needs to finish first. If it still fails, check the head tag with a plain curl rather than a browser: some setups only render the tag client-side, which a scanner that doesn't run JavaScript never sees.

view-source doesn't show the feed link tag even though I added it. What happened?

This is a rendering issue, not a feed issue. On React + Vite the tag lives in the static index.html and should always be in view-source; if it isn't, the wrong file was edited. On TanStack Start, confirm the tag is inside head.links in __root.tsx, not injected client-side with a useEffect or a helmet-style component that runs after the page loads.

Do I need to migrate to TanStack Start just to pass this one check?

No. A static public/feed.xml satisfies E5 on the older template too, at the cost of updating it by hand. Migrate for the E3 and C3 rendering gap first; the feed is a five-minute bonus once you're there.

Does it matter whether I use RSS, Atom, or JSON Feed?

Not for the check. All three pass E5. JSON Feed is the least code to hand-write correctly since it's plain JSON with no XML escaping to get wrong; Atom and RSS both need CDATA or entity-escaped text in titles and descriptions.

How many items should the feed include?

Enough to show a real publishing cadence — roughly 10 to 20 of the most recent items is standard practice across feed readers that consume this format. Fewer than five reads as thin.

Does AIScan check that my publish dates are accurate?

No. It confirms the feed parses and carries a date field in the right format, not that the date matches when you actually published. That part is worth getting right anyway: a feed with fabricated or stale dates misleads any agent using it as a freshness signal.

My feed worked when I tested it locally but 404s after I publish. What happened?

This is almost always the static-file path: a file added to public/ needs an actual publish/deploy cycle to reach the live domain, and a local preview or an unsaved chat session doesn't count. Republish, then re-run the curl check above.

Related guides