Full-Stack Dart for the
JavaScript Ecosystem
Write React, React Native, and Express applications entirely in Dart. One language. Runtime type safety. Sound null safety. No compromises.
// A complete Express server in Dart
import 'package:dart_node_express/dart_node_express.dart';
void main() {
final app = express();
app.get('/', handler((req, res) {
res.jsonMap({'message': 'Hello from Dart!'});
}));
app.listen(3000, () {
print('Server running on port 3000');
}.toJS);
}
dart_node is a collection of Dart packages for full-stack JavaScript development. It provides typed bindings to React, React Native, Express.js, WebSockets, and SQLite. Unlike TypeScript, Dart preserves types at runtime and has sound null safety. Use one language for frontend, backend, and mobile with access to the entire npm ecosystem.
Built for You
Whether you're coming from React or Flutter, dart_node speaks your language.
For React & TypeScript Developers
Same hooks, components, and patterns you know - with runtime type safety TypeScript cannot provide.
const Counter: React.FC = () => {
const [count, setCount] = useState<number>(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count}
</button>
);
};
ReactElement counter() {
final count = useState(0);
return button(
text: 'Count: ${count.value}',
onClick: () => count.setWithUpdater((c) => c + 1),
);
}
Same Paradigms
Hooks, components, props, state — everything you know from React works the same way in Dart.
Runtime Type Safety
Unlike TypeScript, Dart preserves types at runtime. No more any escapes or erased generics.
Simpler Tooling
No webpack, no babel, no tsconfig. Just dart compile js and you're done.
For Flutter Developers
Use your Dart skills for web and Node.js. Access the React and npm ecosystems while writing pure Dart.
class Counter extends StatefulWidget {
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0;
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => setState(() => count++),
child: Text('Count: $count'),
);
}
}
ReactElement counter() {
final count = useState(0);
return button(
text: 'Count: ${count.value}',
onClick: () => count.setWithUpdater((c) => c + 1),
);
}
Same Language
Use your existing Dart skills. Share models, utilities, and business logic across platforms.
Web Ecosystem Access
Leverage the massive React and npm ecosystems while writing pure Dart code.
Full-Stack Dart
Backend (Express), web (React), mobile (React Native) — all in one language.
The dart_node Stack
Packages and tools that give you full-stack superpowers.
dart_node_core
Foundation layer with JS interop utilities, Node.js bindings, and console helpers.
Learn more →dart_node_express
Type-safe Express.js bindings for building HTTP servers and REST APIs.
Learn more →dart_node_react
React bindings with hooks, JSX-like syntax, and full component support.
Learn more →dart_node_react_native
React Native + Expo bindings for cross-platform mobile development.
Learn more →dart_node_better_sqlite3
Typed bindings for better-sqlite3 with synchronous SQLite3 and WAL mode.
Learn more →dart_jsx
JSX transpiler for Dart — write JSX syntax that compiles to dart_node_react calls.
Learn more →dart_node_vsix
VSCode extension API bindings for building Visual Studio Code extensions in Dart.
Learn more →too-many-cooks
Multi-agent coordination MCP server for AI agents editing codebases simultaneously.
Learn more →Dart vs TypeScript: Feature Comparison
A direct comparison of type system capabilities.
Why Types Matter at Runtime
TypeScript erases types when it compiles to JavaScript. Dart doesn't.
interface User {
id: number;
name: string;
}
// At runtime, this is just a plain object
// No way to validate the shape!
const user: User = JSON.parse(data);
// This could fail silently
console.log(user.name.toUpperCase());
// Runtime error if name is undefined!
class User {
final int id;
final String name;
User({required this.id, required this.name});
}
// Types exist at runtime - you can validate!
final user = User.fromJson(jsonDecode(data));
// If name were null, this would fail
// at deserialization, not at usage
print(user.name.toUpperCase());
Get Started in Minutes
# Create a new project
mkdir my_dart_app && cd my_dart_app
dart create -t package .
# Add dart_node packages
dart pub add dart_node_core dart_node_express
# Write your server
cat > lib/server.dart << 'EOF'
import 'package:dart_node_express/dart_node_express.dart';
void main() {
final app = express();
app.get('/', handler((req, res) => res.send('Hello, Dart!')));
app.listen(3000);
}
EOF
# Compile to JavaScript and run
dart compile js lib/server.dart -o build/server.js
node build/server.js