How to Unify Your Multi-Site Web Stack Using Dart and Jaspr: A Step-by-Step Migration Guide
Introduction
Building and maintaining multiple websites with different frameworks and languages can become a significant burden. The Dart team faced this exact challenge: their three primary sites—dart.dev, flutter.dev, and docs.flutter.dev—were each built with separate, non-Dart tools. The documentation sites relied on Eleventy (Node.js), while flutter.dev used Wagtail (Python/Django). This fragmentation made contributions difficult, increased setup friction, and limited code sharing. The solution was to migrate everything to Jaspr, an open-source Dart web framework that supports server-side rendering (SSR), client-side rendering (CSR), and static site generation (SSG). This guide walks you through the same process: from identifying the problem to deploying a unified stack, all using Dart and Jaspr.
What You Need
- Dart SDK (version 3.0 or later) – installed and configured.
- Jaspr CLI – install via
dart pub global activate jaspr_cli. - Existing websites (or a plan to create multiple sites) that you want to unify.
- Basic knowledge of Dart and Flutter widget concepts (optional but helpful).
- A version control system (e.g., Git) to track changes.
- A deployment platform (e.g., Firebase Hosting, Vercel, or your own server) that supports static or server-rendered Dart.
Step-by-Step Migration to Jaspr
Step 1: Assess Your Current Stack and Identify Pain Points
Before migrating, document each site’s current framework, hosting setup, and content model. Ask yourself:
- Which sites use different languages or toolchains? (e.g., Node.js, Python, Ruby)
- How much custom interactivity exists (quizzes, code samples, etc.)?
- What is the contributor experience like? Do you need to learn multiple ecosystems?
In the Dart team’s case, the documentation sites used Eleventy (Node.js) and flutter.dev used Wagtail (Python). This fragmented setup forced contributors to switch contexts and limited code reuse. Identifying these friction points will justify and guide your migration.
Step 2: Choose a Unified Framework – Jaspr
Jaspr is a Dart-based web framework that feels natural to Flutter developers. It supports SSR, CSR, and SSG, making it ideal for a mixed site portfolio. Why Jaspr?
- Flutter skills transfer directly – its component model mirrors Flutter widgets.
- DOM-compatible – you write HTML and CSS but with Dart syntax.
- Open-source and actively maintained.
- No extra toolchain – only Dart is needed.
Step 3: Set Up the Jaspr Project Structure
Create a new Jaspr project for your main site (e.g., jaspr create my_unified_site). This generates a standard structure with lib/, web/, and pubspec.yaml. For multiple sites, consider a monorepo using Dart packages. For example, you can have:
my-sites/
shared-components/
site-dart/
site-flutter/
site-docs/
Each site is a separate Jaspr project that imports widgets from shared-components. This allows you to reuse code across all sites while keeping content separate.
Step 4: Port Your Content and Components to Jaspr
Start with a static version of your most complex page. Jaspr components look like this:
class FeatureCard extends StatelessComponent {
final String title;
final String description;
FeatureCard({required this.title, required this.description, super.key});
@override
Component build(BuildContext context) {
return div(classes: 'feature-card', [
h3([text(title)]),
p([text(description)]),
]);
}
}
If you already have Flutter knowledge, this syntax will be familiar. For each page:
- Convert HTML templates into Jaspr components.
- Port any JavaScript interactivity to Dart (using
dart:htmlor Jaspr’s built-in state management). - Manage media assets and routing using Jaspr’s router or a custom setup.
Step 5: Implement Server-Side Rendering or Static Generation
Jaspr supports both SSR (for dynamic content) and SSG (for fast, cached pages). For documentation sites with mostly static content, SSG works well. For flutter.dev with CMS-like features, SSR might be better. Configure in jaspr.config.yaml:
modes:
static: true # enable static generation for specific routes
server: true # enable server-side rendering for others
You can even combine both: statically generate most pages and use SSR for interactive components like quizzes or code executors.
Step 6: Add Interactivity with Dart
The team wanted richer code samples and quizzes. With Jaspr, you can add client-side interactivity using Dart event handlers and state management. For example, a quiz component:
class Quiz extends StatefulComponent {
@override
State createState() => _QuizState();
}
class _QuizState extends State {
int score = 0;
void answerCorrect() {
setState(() => score++);
}
@override
Component build(BuildContext context) {
return div([
button(onClick: answerCorrect, [text('Correct')]),
p([text('Score: $score')]),
]);
}
}
This eliminates the need for separate JavaScript files and keeps all logic in Dart.
Step 7: Test, Iterate, and Deploy
Run jaspr serve locally to preview your unified sites. Ensure all links, assets, and interactive features work across devices. Once satisfied, build for production:
jaspr build
This generates a build/ folder with static files (if using SSG) or a server bundle (if using SSR). Deploy to your preferred platform. The Dart team used the same deployment pipelines but simplified them because only one toolchain was needed.
Tips for a Smooth Migration
- Start small – migrate one site first, then the others. This reduces risk and lets you refine your workflow.
- Leverage shared components – create a package of reusable widgets (headers, footers, cards) to avoid duplication.
- Keep your existing content – Jaspr can consume Markdown or YAML files if you want to keep content separate from code.
- Use version control – commit often and use feature branches for each site migration.
- Engage with the community – Jaspr is open-source; check their GitHub for examples and ask questions.
- Test interactivity thoroughly – since you’re moving from imperative JavaScript to declarative Dart, edge cases may arise.
- Monitor performance – compare Lighthouse scores before and after migration to ensure no degradation.
- Document the new architecture – help your team and future contributors understand the unified stack.
By following these steps, you can replicate the Dart team’s success: a unified, Dart-only web stack that simplifies contributions, reduces cognitive load, and opens the door to richer interactive features. Jaspr makes it possible to bring the power of Flutter to the DOM without leaving your language.
Related Discussions