Flutter's web target has quietly gotten good. flutter build web spits out a folder of HTML, JS, and WASM that runs the same widget tree you ship to iOS and Android. It's a static bundle — which means hosting it is genuinely simple, *if* you get three things right: the renderer choice, single-page-app routing, and cache headers. I run PandaStack, so I'll deploy one here, but the build steps are the same wherever you host.
Step 1: Build for web
flutter build web --releaseThe output lands in build/web/. That directory is your entire deployable — index.html, a main.dart.js, and asset files. No server required.
The renderer decision (do this consciously)
Flutter web can render two ways, and the default has shifted across versions, so pick on purpose:
- CanvasKit (WASM-based Skia): pixel-identical to mobile, great for graphics-heavy or canvas-drawing apps, but ships a larger initial payload (the Skia WASM).
- HTML renderer: lighter, faster first paint, better for text-heavy content sites, but less fidelity for complex custom painting.
Force one explicitly if you care:
flutter build web --release --web-renderer canvaskit
# or
flutter build web --release --web-renderer htmlRule of thumb: content/marketing app → HTML renderer for speed; interactive/graphical app → CanvasKit for fidelity. The official guidance lives at https://docs.flutter.dev/platform-integration/web/renderers.
Step 2: Fix SPA routing before you deploy
Flutter web uses client-side routing. If a user hard-refreshes on /dashboard, the host must still serve index.html or they get a 404. On PandaStack's static site hosting, SPA fallback is handled for you — every unknown path serves index.html, so flutter's router picks it up. No .htaccess, no Nginx try_files, nothing to configure.
One caveat: use path URL strategy, not the default hash strategy, for clean URLs. In main.dart:
import 'package:flutter_web_plugins/url_strategy.dart';
void main() {
usePathUrlStrategy();
runApp(const MyApp());
}Step 3: Deploy on PandaStack
Two ways.
Dashboard: https://dashboard.pandastack.io → New Site → Static → connect your repo. Set:
| Setting | Value |
|---|---|
| Build command | flutter build web --release |
| Output directory | build/web |
If Flutter isn't in the default build image, commit a tiny build script or use a Dockerfile-based container build; but for most repos the static build with a specified command works.
CLI:
npm install -g @pandastack/cli
panda login
flutter build web --release
panda deploy --type static --dir build/webEither way, every push to your default branch rebuilds and redeploys. SSL on the *.pandastack.io domain (and any custom domain you add) is automatic.
Step 4: Caching — the one manual optimization worth doing
Flutter web fingerprints most assets, but index.html and the service worker must not be cached long, or users get stuck on a stale version after you deploy. The good news: flutter build already writes a flutter_service_worker.js that manages versioned asset caching. Your job is just to make sure the HTML entry point is served fresh. PandaStack serves static assets with sensible defaults and gzip/Brotli compression out of the box, so in practice you deploy and it works — but if you see users on old builds, hard-refresh clears it and the service worker updates on next visit.
Step 5: Custom domain
App → Domains → Add Domain → add a CNAME at your DNS provider pointing to the shown hostname. SSL provisions automatically once DNS resolves.
Gotchas I've hit
- Big initial download. CanvasKit apps can feel heavy on first load. Enable
--release(never ship a debug build), consider the HTML renderer for content apps, and lazy-load heavy screens. - CORS on your API. If your Flutter web app calls a separate API, that API must send CORS headers — the browser enforces this even though your mobile build never cared.
base href. If you deploy under a subpath, set--base-href /subpath/. Deploying at the domain root (the normal case) needs nothing.
Honest note
Flutter web is excellent for app-like experiences and dashboards. For a pure content/SEO site, a framework that server-renders HTML will out-SEO a Flutter SPA — Flutter web content is JS-rendered, and while crawlers execute JS, HTML-first frameworks still have the edge. Choose Flutter web when the *interactivity* is the point.
Wrap-up
flutter build web → static folder → PandaStack static hosting with automatic SPA fallback, SSL, and compression. Pick your renderer deliberately, use the path URL strategy, and you're live.
Docs: https://docs.pandastack.io. Start free: https://dashboard.pandastack.io.