skip to content

CanvasKit & Skwasm

Flutter web draws with CanvasKit or, under flutter build web --wasm, skwasm, and reaches the page through HtmlElementView and dart:js_interop. Interviewers probe download size, SEO and interop.

part ofFlutteroverview, primer and where to startread it →
on this pageshow

explore

questions

6

Which renderers does Flutter 3.47 use on the web, and how does an app end up on CanvasKit or skwasm?

level: juniorimportance: must knowfreq 52%

answer

  1. both are Skia compiled to WebAssembly
  2. plain build web means dart2js
  3. --wasm adds a dart2wasm build
  4. the loader picks at page load
  5. HTML renderer removed, flag gone in 3.29

basics

~20 s

Flutter web draws with Skia in two builds: CanvasKit, used by the default JavaScript output of flutter build web, and skwasm, used when the app is built with --wasm and the browser can run it. The HTML renderer is gone.

solid answer

~40 s

Flutter 3.47 has two web renderers, both Skia compiled to WebAssembly and drawing into a canvas, not into HTML elements; Impeller is not used on the web. **CanvasKit** pairs with the JavaScript output of a plain `flutter build web` (dart2js). **skwasm** pairs with the dart2wasm output of `flutter build web --wasm`; it talks to the Wasm-compiled app directly and can render on a web worker when the page is cross-origin isolated. A `--wasm` build also contains a JS + CanvasKit build, and the `flutter.js` loader picks one at page load: skwasm when the browser supports WasmGC and WebGL and its engine is allowed, otherwise the JS fallback. The old DOM-based **HTML renderer** was deprecated and removed, and the `--web-renderer` flag was deleted in Flutter 3.29.

go deeper

for a junior

Recall the two renderers, CanvasKit for the JavaScript build and skwasm for the --wasm build, and that the HTML renderer is gone.

for a middle

Explain how the loader chooses between the two builds at page load and which browser features skwasm needs.

for a senior

Show you know what each choice costs in download size, threading and browser coverage, and how to confirm which build users actually run.

for a principal

Relate the canvas-based rendering model to product decisions: which surfaces suit Flutter web and which need document-style HTML.

## What a web renderer is in Flutter On Android and iOS the Flutter engine paints every pixel with its own renderer. On the web the engine does the same thing inside a browser tab: the framework builds its layer tree exactly as on mobile, and a **web renderer** turns that tree into pixels on a `<canvas>`. Flutter does not translate widgets into HTML elements such as `<div>` or `<button>`. In Flutter 3.47 both web renderers are builds of **Skia**, Flutter's long-standing 2D graphics library, compiled to WebAssembly. **Impeller**, the renderer on iOS, on Android API 29+ and, since 3.47, on desktop, is not used on the web. ## The two renderers | | CanvasKit | skwasm | |---|---|---| | Paired with | JavaScript output (dart2js) | WebAssembly output (dart2wasm) | | Produced by | `flutter build web` | `flutter build web --wasm` | | Browser needs | WebAssembly; WebGL unless forced to CPU-only | **WasmGC**, WebGL, an allowed browser engine | | Threads | main thread | can render on a web worker when the page is cross-origin isolated | | Download | `canvaskit.wasm`, variant chosen by `canvasKitVariant` | `skwasm.wasm`, from the same engine-files location | Key points: - **CanvasKit** is the renderer of the default JavaScript build. Its runtime setting `canvasKitVariant` defaults to `auto`; `chromium` downloads a smaller variant for Chromium-only audiences. - **skwasm** exists because a Wasm-compiled Dart app can call a Wasm renderer without going through JavaScript. It is selected only when the app itself was compiled with dart2wasm. - By default the tool points the loader at a Google-hosted CDN for these engine files; `--no-web-resources-cdn` serves them from your own build output. ## How an app ends up on one or the other 1. **Build time.** `flutter build web` produces only the JavaScript build, so the app always runs on CanvasKit. `flutter build web --wasm` produces two builds: dart2wasm with skwasm, plus a dart2js build with CanvasKit as a fallback. The tool refuses to combine `--wasm` with a non-default renderer. 2. **Page load.** The generated `flutter_bootstrap.js` calls `_flutter.loader.load()`, which walks the builds in order and takes the first one the browser can run. skwasm is skipped if the browser lacks WasmGC or WebGL, or if its engine is not on the Wasm allow list, which by default enables only Chromium-based browsers. 3. **Result.** The same deployment can run skwasm in desktop Chrome and CanvasKit in Safari or Firefox. Code can check which compiler produced the running app with `const bool.fromEnvironment('dart.tool.dart2wasm')`. ## The removed HTML renderer Older Flutter versions offered an **HTML renderer** that mapped drawing onto DOM elements and CSS, chosen with `--web-renderer html`. It rendered inconsistently with mobile and was deprecated; Flutter 3.29 removed the `--web-renderer` flag from the tool and the renderer from the docs. Answers that recommend it for smaller downloads or better text describe a Flutter that no longer exists. ## Controlling the choice - `flutter build web --wasm` is the only switch most teams need; the renderer follows the compile target. - The loader's `config` in `flutter_bootstrap.js` accepts a `renderer` value, which makes the loader skip builds for any other renderer, and CanvasKit settings such as `canvasKitVariant`, `canvasKitBaseUrl` and `canvasKitForceCpuOnly`. - If you write a custom `onEntrypointLoaded`, pass that configuration to `initializeEngine()` yourself; the loader forwards it automatically only when you let it start the app. ## Why interviewers ask - It checks you know Flutter web is canvas-based, which explains its SEO, text-selection and accessibility behaviour. - It checks you know the renderer is a consequence of the **compile target**, not a separate switch. - It flushes out stale knowledge: "HTML for mobile browsers, CanvasKit for desktop" was the old auto mode, not the current one.

  • A Flutter team built with --wasm, yet Safari users get CanvasKit. Is the deployment broken?
    No. The `flutter.js` loader tries the dart2wasm + skwasm build first and falls back to the dart2js + CanvasKit build when the browser lacks WasmGC or WebGL or its engine is not on the Wasm allow list, which by default enables only Chromium-based browsers. Serving both builds is the intended behaviour.
  • How can a Flutter web app tell at runtime whether it was compiled to Wasm?
    Read the compile-time constant `const bool.fromEnvironment('dart.tool.dart2wasm')`, which dart2wasm sets. The loader has already chosen a build before Dart code runs, so this answers which build the page actually loaded.

saying these in an interview costs you the question

  • Flutter web turns widgets into HTML elements like div and button.
  • The HTML renderer is still the default on mobile browsers.
  • Impeller renders Flutter web apps in 3.47.
  • You pick skwasm with --web-renderer skwasm.
  • A --wasm build fails to start in browsers without WasmGC.
open as a page

In a Flutter web app, how do you embed a real HTML element such as an iframe, and what does it cost?

level: middleimportance: should knowfreq 33%

basics

~20 s

HtmlElementView reserves layout space for a DOM element: fromTagName creates it and onElementCreated configures it through package:web. The element receives pointer events Flutter never sees, and every one splits the canvas into overlays that cost memory and speed.

open as a page

Why do Flutter web URLs contain #/ by default, and what does switching to usePathUrlStrategy require in the app and on the server?

level: middleimportance: should knowfreq 42%

basics

~20 s

Flutter web defaults to the hash URL strategy, keeping routes after # so any static server works. usePathUrlStrategy from flutter_web_plugins, called before runApp, gives clean paths, but the server must rewrite unknown paths to index.html.

open as a page

What does Flutter's flutter build web --wasm produce, when do browsers fall back to JavaScript, and why do COOP and COEP headers matter?

level: middleimportance: should knowfreq 40%

basics

~20 s

flutter build web --wasm emits a dart2wasm build on skwasm plus a dart2js build on CanvasKit. Browsers without WasmGC, WebGL or default Wasm enablement get the JavaScript build. Without COOP and COEP headers skwasm still runs, but single-threaded.

open as a page

Why does a Flutter web app index poorly in search engines and load slower on first visit than a plain HTML page, and what can you do?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Flutter web paints into a canvas, so crawlers find little document HTML, and the semantics DOM is off by default. First load must fetch the renderer's Wasm and the compiled app. Keep searchable content in HTML and use Flutter for app-like surfaces.

open as a page

A delivery service has a Flutter courier app and needs an admin dashboard and public order-tracking pages; which would you build with Flutter web, and which as a separate web app?

level: principalimportance: should knowfreq 28%

basics

~20 s

The admin dashboard suits Flutter web: logged-in, app-like, used on desktop, able to share Dart code with the courier app. Public tracking pages need fast first paint, link previews and search, so they fit a lightweight HTML page.

open as a page