Same Flutter Code. Real HTML Output.
For when your Flutter app needs to be found by Google.
Installation β’ Quick Start β’ Why FlutterJS? β’ Documentation
Flutter Web is amazing for apps, but terrible for the web:
| Flutter Web Issue | Impact |
|---|---|
| π¦ 2-5+ MB bundle | Slow initial load, especially on mobile |
| π Zero SEO | Canvas rendering = invisible to Google |
| βΏ Poor accessibility | No semantic HTML for screen readers |
| π’ Slow first paint | WASM/CanvasKit takes time to initialize |
FlutterJS compiles your Flutter/Dart code to semantic HTML + CSS + JavaScript instead of Canvas/WASM.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β YOUR FLUTTER CODE β
β β
β Scaffold( β
β appBar: AppBar(title: Text('My App')), β
β body: Center(child: Text('Hello World')), β
β ) β
βββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββ΄ββββββββββββββββ
βΌ βΌ
Flutter Web FlutterJS
βββββββββββββββ βββββββββββββββ
β <canvas> β β <header> β
β (pixels) β β <main> β
β 5 MB β β <h1> β
β No SEO β β 50 KB β
βββββββββββββββ β Full SEO β β
βββββββββββββββ
| Feature | Flutter Web | FlutterJS |
|---|---|---|
| Code Syntax | Flutter/Dart | Flutter/Dart β |
| Bundle Size | 2-5 MB | ~50-100 KB |
| SEO | β None | β Full |
| Google Indexable | β No | β Yes |
| Accessibility | Poor | Good |
| Initial Load | 3-8 seconds | <1 second |
| SSR Support | β No | β Yes |
| Output | Canvas pixels | Real HTML |
To develop with or use FlutterJS, you need:
- Dart SDK:
^3.10.0or higher (Stable). - Node.js: Required for the JavaScript engine and CLI tools.
- NPM: Package manager for JS dependencies.
- Git: For managing the monorepo and submodules.
- Flutter SDK: (Optional but recommended) For resolving SDK dependencies.
npm install -g flutterjsgit clone --recursive https://github.com/flutterjsdev/flutterjs.git
cd flutterjs
dart pub global activate --source path .
dart pub get
dart run tool/init.dartβ
CI/CD Stabilized: Full automation with GitHub Actions, supporting recursive submodules and workspace resolution.
β
Ecosystem Launch: First official package flutterjs_seo is now live on pub.dev.
β
Modern Dart Support: Fully compatible with Dart 3.10+ features including dot shorthand and records.
β
Monorepo Readiness: Standardized workspace structure across all 20+ packages.
flutter create my-app
cd my-app// src/main.dart
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('FlutterJS App')),
body: Center(
child: Text('Hello from FlutterJS!'),
),
),
);
}
}flutterjs run --to-js --serveOpen http://localhost:3000 β inspect the page to see real HTML elements, not canvas!
# Build your application
flutterjs buildThis creates a dist/ directory with:
index.html(Entry point)assets/(Static resources)main.js(Compiled app)vercel.json(Deployment config)
Since flutterjs build automatically generates vercel.json, deployment is zero-config:
# Using Vercel CLI
vercel deployOr connect your GitHub repository to Vercelβit will automatically detect the output.
Deployment is zero-config and optimized for cleanliness (no duplicate node_modules).
-
Build:
flutterjs build
(Creates
dist/with app files, keeps dependencies in root) -
Deploy:
cd ./build/flutterjs vercel deploy --prod
The build automatically generates vercel.json and .vercelignore to ensure:
- Routing: SPAs work correctly (all routes β
index.html) - Dependencies:
node_modulesare uploaded efficiently - Cleanliness: Your project remains unpolluted
You can deploy the contents of build/flutterjs/dist/ to any static host (Netlify, GitHub Pages, Firebase Hosting).
Note: Ensure your provider handles SPA routing (redirect 404s to index.html).
FlutterJS intercepts kIsWeb platform checks and routes web-specific code through a lightweight JavaScript runtime instead of Flutter's Canvas/WASM engine.
// Your existing Flutter code
if (kIsWeb) {
// FlutterJS handles this path
// Converts to semantic HTML + CSS
}Flutter/Dart Source
β
βΌ
βββββββββββββββββββββ
β FlutterJS Parser β β Analyzes Dart AST
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β Code Generator β β Transforms to JavaScript
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β JS Runtime β β VNode/VDOM engine
βββββββββββ¬ββββββββββ
β
βΌ
βββββββββββββββββββββ
β HTML + CSS β β Semantic, SEO-friendly output
βββββββββββββββββββββ
| Command | Description |
|---|---|
flutterjs init <name> |
Create new project |
flutterjs dev |
Start dev server with hot reload |
flutterjs build |
Production build with minification |
flutterjs preview |
Preview production build |
flutterjs --help |
Show all commands |
flutterjs build --mode ssr # Server-side rendering
flutterjs build --mode csr # Client-side rendering (default)
flutterjs build --mode hybrid # Best of both
flutterjs build -O 0 # Debug build (No optimization / No minification)
flutterjs build -O 3 # Production build (Aggressive optimization)
flutterjs build --output ./dist # Custom output directoryApplication renders in the browser. Good for SPAs.
Application renders entirely in the browser using JavaScript.
- Best for: Dynamic web apps, Dashboards, Admin panels.
- CLI:
flutterjs run --target spa(or justflutterjs run) - Config:
mode: 'csr'
Pre-renders HTML on the server (build time) and hydrates on the client.
- Best for: Marketing sites, Blogs, SEO-critical content.
- CLI:
flutterjs run --target ssr - Config:
mode: 'ssr' - How it works:
- Build generates a pre-rendered
index.html. - Client downloads HTML (instant paint).
- Client hydrates (attaches event listeners).
- Build generates a pre-rendered
A mix of Static Site Generation (SSG) and SPA.
- Best for: Large sites with mixed content.
- CLI:
flutterjs run --target hybrid - Config:
mode: 'hybrid' - Note: Currently experimental. Use SSR for best SEO results.
FlutterJS supports the most commonly used Flutter widgets:
Container,Center,Padding,SizedBoxRow,Column,Stack,PositionedExpanded,Flexible,Spacer
Scaffold,AppBar,DrawerElevatedButton,TextButton,IconButton,FloatingActionButtonText,Icon,ImageCard,DividerTextField,Checkbox,Switch
Navigator,MaterialPageRoute
StatefulWidget,setState()InheritedWidget
More widgets are being added regularly.
β Use FlutterJS if:
- Your Flutter app needs SEO (blogs, e-commerce, marketing sites)
- You're targeting low-bandwidth users (smaller bundles matter)
- You need Google to index your content
- You want semantic HTML for accessibility
- First-load performance is critical
β Stick with Flutter Web if:
- You're building a complex app (games, graphics-heavy)
- SEO doesn't matter (internal tools, dashboards)
- You need every Flutter widget/feature
| Metric | Flutter Web | FlutterJS |
|---|---|---|
| Bundle Size | 2-5 MB | ~50-100 KB |
| First Paint | 3-8s | <1s |
| Time to Interactive | 5-10s | <2s |
| Lighthouse SEO | 0-30 | 90-100 |
Create flutterjs.config.js in your project root:
module.exports = {
// Rendering mode
mode: 'csr', // 'ssr' | 'csr' | 'hybrid'
// Build settings
build: {
output: 'dist',
minify: true,
sourcemap: false,
},
// Dev server
server: {
port: 3000,
hot: true,
},
// Optimization
optimization: {
treeshake: true,
splitChunks: true,
}
};my-app/
βββ flutter.config.js # Configuration
βββ package.json
βββ src/
β βββ main.dart # Your Flutter code
βββ assets/ # Static files (images, fonts)
βββ .flutter_js/ # Generated code (auto)
βββ dist/ # Production build (generated)
FlutterJS includes a powerful Dart CLI that analyzes your Flutter/Dart code and converts it to optimized JavaScript.
# Navigate to your Flutter project
cd examples/counter
# Run the full pipeline: Analysis β IR β JavaScript
dart run path/to/flutterjs/bin/flutterjs.dart run --to-js
# With dev server (starts after conversion)
dart run path/to/flutterjs/bin/flutterjs.dart run --to-js --serve
# With custom port
dart run path/to/flutterjs/bin/flutterjs.dart run --to-js --serve --server-port 4000The Dart CLI executes a multi-phase pipeline:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PHASE 0: Setup & Initialization β
β β’ Validate project directory β
β β’ Initialize Dart analyzer β
β β’ Create output directories β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β PHASE 1: Static Analysis β
β β’ Parse Dart AST β
β β’ Build dependency graph β
β β’ Detect changed files (incremental) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β PHASE 2: IR Generation (5 Passes) β
β β’ Pass 1: Declaration Discovery β
β β’ Pass 2: Symbol Resolution β
β β’ Pass 3: Type Inference β
β β’ Pass 4: Control-Flow Analysis β
β β’ Pass 5: Validation & Diagnostics β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β PHASE 3: IR Serialization β
β β’ Generate binary IR files β
β β’ Save conversion reports β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β PHASE 4-6: JavaScript Conversion β
β β’ Convert IR to JavaScript β
β β’ Validate generated code β
β β’ Optimize (levels 0-3) β
β β’ Write .fjs files β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β PHASE 7: Dev Server (--serve flag) β
β β’ Spawn flutterjs.exe dev server β
β β’ Open browser automatically β
β β’ Hot reload on file changes β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Flag | Description | Default |
|---|---|---|
--project, -p |
Path to Flutter project root | . |
--source, -s |
Source directory relative to project | lib |
--to-js |
Convert IR to JavaScript | false |
--serve |
Start dev server after conversion | false |
--server-port |
Dev server port | 3000 |
--open-browser |
Open browser automatically | true |
--js-optimization-level |
Optimization level (0-3) | 1 |
--validate-output |
Validate generated JavaScript | true |
--incremental |
Only reprocess changed files | true |
--parallel |
Enable parallel processing | true |
--verbose, -v |
Show detailed logs | false |
FlutterJS uses a bridge architecture to connect the Dart CLI with the JavaScript runtime engine.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Dart CLI (flutterjs.dart) β
β β
β β’ Analyzes Dart/Flutter code β
β β’ Generates IR (Intermediate Representation) β
β β’ Converts IR to .fjs JavaScript files β
β β
β After conversion (when --serve is used): β
β β β
β βΌ Process.start() β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β flutterjs.exe dev --port 3000 β β
β β β β
β β β’ Serves .fjs files via Express.js β β
β β β’ Hot Module Replacement (HMR) β β
β β β’ WebSocket for live updates β β
β β β’ Opens browser automatically β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Browser: http://localhost:3000 β β
β β β β
β β β’ FlutterJS Runtime loads β β
β β β’ Widgets render to semantic HTML β β
β β β’ State management works β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The engine bridge automatically detects your platform and uses the appropriate binary:
| Platform | Binary |
|---|---|
| Windows | flutterjs.exe |
| macOS | flutterjs-macos |
| Linux | flutterjs-linux |
Binaries are located in: packages/flutterjs_engine/dist/
cd packages/flutterjs_engine
# Build for current platform
npm run build:windows # Windows
npm run build:macos # macOS
npm run build:linux # Linux
# Build for all platforms
npm run build:allThe bridge is designed to evolve to a full IPC (Inter-Process Communication) mode:
// Future IPC implementation
Process.start('flutterjs.exe', ['dev', '--ipc']);
// stdin: {"method": "reload", "files": ["main.fjs"]}
// stdout: {"status": "ok", "reloadedCount": 1}The Dart CLI now automatically generates a proper JS project structure in build/flutterjs/:
your-flutter-project/
βββ lib/
β βββ main.dart β Your Flutter/Dart source code
β
βββ build/
βββ reports/ β Dart CLI reports (analysis, conversion)
β βββ conversion_report.json
β βββ summary_report.json
β
βββ flutterjs/ β Generated JS project (where JS CLI runs)
βββ flutterjs.config.js β Auto-generated config
βββ package.json β Auto-generated manifest
βββ src/ β Generated .fjs files
β βββ main.fjs
βββ public/ β Generated HTML
βββ index.html
How It Works:
- Dart CLI runs from your project root (
examples/counter) - Dart CLI analyzes
lib/main.dartand converts it tobuild/flutterjs/src/main.fjs - Dart CLI auto-generates
flutterjs.config.js,package.json, andpublic/index.html - JS CLI runs from
build/flutterjs/(the generated JS project) - Browser opens and your app is running!
cd examples/counterdart run ../../bin/flutterjs.dart run --to-js --serveββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FLUTTER IR TO JAVASCRIPT CONVERSION PIPELINE (Phases 0-6) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Project: C:\path\to\flutterjs\examples\counter
Source: C:\path\to\flutterjs\examples\counter\lib
Build: C:\path\to\flutterjs\examples\counter\build\flutterjs
PHASE 1: Analyzing project...
Files for conversion: 2
PHASE 2: Generating IR...
β
main.dart processed
PHASES 4-6: Converting IR to JavaScript...
β
Generated: 1 files
οΏ½ Setting up FlutterJS project...
β
JS project initialized
οΏ½π Starting FlutterJS Dev Server...
β
Dev Server running at http://localhost:3000
π Project root: C:\path\to\flutterjs\examples\counter\build\flutterjs
π Source files: C:\path\to\flutterjs\examples\counter\build\flutterjs\src
β³ Server(s) running. Press "q" or Ctrl+C to stop.
π Dev Server: http://localhost:3000
- Core widget system (StatelessWidget, StatefulWidget)
- Material Design components
- CLI with dev server
- SSR/CSR/Hybrid modes
- Dart CLI Pipeline (Analysis β IR β JS)
- Engine Bridge (Dart CLI β JS Runtime)
- Incremental compilation
- DevTools IR Viewer
- Dart Core Libraries (
dart:math,dart:async,dart:convert, etc.) - Animation support
- Full Material 3 theming
- Route-based code splitting
- PWA support
- TypeScript definitions
- IPC mode for tighter CLI-Engine integration
Currently, passing methods directly as callbacks (tear-offs) may causing binding issues where this becomes undefined.
Workaround: Wrap callbacks in a lambda to preserve context.
// β Avoid (may fail based on transpiler version)
onPressed: _incrementCounter
// β
Recommended
onPressed: () => _incrementCounter()To set up the project for development (both Dart and JavaScript packages), run the following command from the root directory:
# 1. Get Dart dependencies
dart pub get
# 2. Initialize project (installs JS dependencies)
dart run tool/init.dartContributions are welcome! See CONTRIBUTING.md for guidelines.
# Clone the repo
git clone https://github.com/flutterjsdev/flutterjs.git
# Set up the project (Follow 'Initialization' steps above)
# 1. dart pub get
# 2. dart run tool/init.dartBSD 3-Clause "New" or "Revised" License β see LICENSE for details.
- Website: flutterjs.dev
- GitHub: github.com/flutterjsdev/flutterjs
- Issues: Report a bug
- Discussions: Ask questions
FlutterJS
Write Flutter. Ship the Web.