Flutter Web SEO: 5 Costs of a Canvas-Only Site
A team ships a Flutter app to the web, opens view-source out of habit, and finds a <canvas> and a script tag. The product looks right in a browser. Google receives an empty shell, and the same is true for every link preview in a chat. Flutter web seo is decided at that moment, long before anyone writes a title tag.
Code: HydraLine, MIT licensed.
HydraLine puts real semantic HTML into the first HTTP response and hydrates interactive islands on top. It works with an existing Flutter Web app, one route at a time, without replacing main() or the router.
Why flutter web seo fails by default
Flutter paints the application into a canvas. The DOM carries one element and a loader. A crawler that does not execute the framework sees a page with no heading, no text and no links.
Script execution does not save it either. Even a crawler that renders JavaScript gets a canvas, and text inside a canvas cannot be selected, quoted or indexed as text. The problem is structural, and no meta tag fixes a structure.
What the first response can carry
In document mode the response contains headings, paragraphs, lists and links, plus canonical, hreflang, Open Graph and Twitter Card with image dimensions, and nine JSON-LD types. The sitemap generator splits at 50 000 addresses, which matters because a large catalogue crosses that line before anyone notices.
The same document tree streams from a Dart server or compiles into a static dist/ folder, so a marketing site can be static and an app can be served, from one model. Two rendering paths from one source is the part that keeps them honest.
Cost 1: traffic that never had a chance
A page that ranks on nothing earns nothing. For a product whose acquisition depends on search, a canvas-only site means the whole content surface is invisible, and the team keeps buying traffic that a competitor gets for free.
Cost 2: link previews that show a blank card
Every shared link in Slack, Telegram, LinkedIn or WhatsApp reads Open Graph tags. Without them the preview is a grey rectangle with a bare URL, and a product launch looks broken in the channel where it is announced.
Cost 3: paid traffic landing on an invisible page
Ads do not need SEO, they need a landing page that loads fast and converts. A canvas page that takes seconds to paint its first frame loses the click it paid for, and the campaign report blames the audience rather than the page.
Cost 4: the rewrite nobody budgeted
The obvious cure is a second frontend in another framework for the public pages, and that is a second codebase, a second deploy and a second set of bugs. The cost lands in maintenance every month, long after the project closes.
HydraLine is additive: a route can move to document mode, keep the Flutter widgets elsewhere, and the rest of the app keeps working. hybrid mode puts semantic HTML around islands that still run Flutter, so a product page with a configurator does not need two teams.
Cost 5: the cloaking shortcut
The cheapest hack is to serve one page to a bot and another to a person, sniffing User-Agent. It works until someone compares the two responses, and then it is a search penalty on the domain, not on the page.
Here the builders cannot read User-Agent at all, so bot and human receive byte-identical bodies. A test in CI compares the two responses, which is the only version of this promise that survives a refactor.
What a visitor gets while the engine loads
Interactivity comes in three levels. L0 is static HTML with no JavaScript: details and summary elements, :target links, lazy images. L1 adds vanilla JavaScript and HTMX islands of about 8–14 KB, enough for tabs, forms and search. L2 loads the Flutter engine, and only when an island actually needs it.
Most content pages never reach L2. A blog post, a documentation page or a landing page stays at L0 or L1, and the engine never loads. Layout shift stays near zero because each island reserves its exact size through Declarative Shadow DOM.
What it costs to adopt
An afternoon per route for the pages that matter, and no work at all for the pages that do not. 486 unit and widget tests, 26 end-to-end tests in a real Chrome, an XSS fuzz suite of a million inputs and an audit CLI ship with the package, which is the part a client pays for once and keeps.
Related: mobile development for how a Flutter scope is quoted, and seo content audit for the content check that starts once the page is crawlable.
