name: "building-jaspr-web-apps" description: "Builds SEO-friendly web applications with Dart using the Jaspr framework. Use when creating Dart-based websites, blogs, or landing pages requiring semantic HTML output; implementing server-side rendering (SSR) or static site generation (SSG); adding Tailwind CSS via jaspr_tailwind; routing with jaspr_router; testing components with jaspr_test; or building full-stack Dart web apps where Flutter Web's canvas rendering is unsuitable." metadata: last_modified: "2026-04-27 17:41:00 (GMT+8)"
Jaspr Web Architecture Guide (v0.22.x)
Goal
Jaspr is a Dart web framework (current version: 0.22.x).
Unlike Flutter Web (which draws pixels via Canvas and produces poor SEO), Jaspr compiles Dart to semantic HTML and CSS DOM elements. Use Jaspr for websites, blogs, landing pages, or any project requiring proper SEO.
Instructions
1. Core Concept: Flutter-like Coding Experience
Jaspr mirrors Flutter’s component model. The build method returns a single Component (equivalent to a Flutter Widget).
| Flutter Concept | Jaspr Concept | Notes |
|---|---|---|
Widget | Component | Every UI element in Jaspr is a Component |
StatelessWidget | StatelessComponent | Pure declarative UI; receives BuildContext context |
StatefulWidget | StatefulComponent | Manages lifecycle with setState, initState, createState() |
InheritedWidget | InheritedComponent | Propagates state down the component tree |
1.1 DOM Elements
Jaspr renders to DOM, so use its HTML tag helpers (div, p, img, etc.) from jaspr/dom.dart.
import 'package:jaspr/jaspr.dart';
class ProfileCard extends StatelessComponent {
final String name;
const ProfileCard({required this.name, super.key});
@override
Component build(BuildContext context) {
return div(
classes: 'profile-card',
styles: Styles.box(width: 300.px, backgroundColor: Colors.white),
[
h2([.text(name)]), // 🌟 .text() shorthand available since Dart 3.10 (Component.text)
p([
.text('Hello World!'),
]),
button(
onClick: () => print('Button successfully clicked!'),
[.text('Contact Me')]
)
]
);
}
}
2. Tailwind CSS Integration (jaspr_tailwind)
Jaspr provides official Tailwind CSS integration via jaspr_tailwind.
2.1 Installation & Setup
dev_dependencies:
jaspr_tailwind: ^0.3.6
Create web/styles.tw.css with the Tailwind v4 CSS-first syntax:
/* web/styles.tw.css */
@import "tailwindcss";
2.2 Loading Compiled CSS in the Root Component
In the root component, use Document to link the compiled stylesheet and consume Tailwind classes via the classes parameter:
import 'package:jaspr/jaspr.dart';
class App extends StatelessComponent {
@override
Component build(BuildContext context) {
return Document(
head: [
// 🌟 jaspr_tailwind auto-compiles styles.tw.css → styles.css
link(href: 'styles.css', rel: 'stylesheet'),
],
body: div(
// Use Tailwind classes directly; jaspr_tailwind auto-refreshes during development
classes: 'bg-gray-100 min-h-screen flex items-center justify-center',
[
h1(classes: 'text-4xl font-bold text-blue-600', [.text('Hello Tailwind!')]),
]
),
);
}
}
3. Routing (jaspr_router)
Jaspr’s routing mirrors go_router with a declarative API.
3.1 Installation & Setup
dependencies:
jaspr_router: ^0.8.2
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_router/jaspr_router.dart';
class MyApp extends StatelessComponent {
@override
Component build(BuildContext context) {
return Router(
routes: [
Route(
path: '/',
builder: (context, state) => const HomePage(),
),
Route(
path: '/about',
builder: (context, state) => const AboutPage(),
),
],
);
}
}
4. SSR: Asynchronous Backend Data Preloading
Jaspr’s key advantage over Flutter Web is server-side rendering (SSR). With Flutter Web, data is fetched in initState, causing a loading state on the client. With Jaspr SSR, data is fetched on the server before the HTML is sent to the browser.
4.1 AsyncStatelessComponent (Server-only)
Import jaspr/server.dart to use AsyncStatelessComponent, which allows an async build method.
import 'package:jaspr/server.dart'; // 🌟 Server-only import
class ArticlePage extends AsyncStatelessComponent {
final String articleId;
const ArticlePage(this.articleId);
@override
Future<Component> build(BuildContext context) async {
// Runs server-side; freely use await for database/API calls
final articleData = await Database.fetchArticle(articleId);
// Browser receives fully-rendered HTML for instant, SEO-friendly content
return div([
h1([.text(articleData.title)]),
p([.text(articleData.content)]),
]);
}
}
4.2 Stateful Preloading (PreloadStateMixin)
When state must be preserved, mix in PreloadStateMixin:
class MyState extends State<MyStatefulComponent> with PreloadStateMixin {
@override
Future<void> preloadState() async {
// Runs server-side before initState(); prepares state for client-side hydration
await loadInitialData();
}
// ...
}
5. Testing (jaspr_test)
jaspr_test covers component unit tests, SSR results, and browser interactions.
5.1 Dependency Setup
dart pub add jaspr_test --dev
Since jaspr_test re-exports the native package:test, there is no need to import it separately.
5.2 Three Major Testing Functions
Due to the complexities of the HTML/Web environment, Jaspr provides three scenario-specific testing functions:
testComponents (Virtual Environment Component Testing)
Unit test your Component logic in a Headless, pure Dart simulated environment. This is the fastest method. Usage is highly similar to Flutter's testWidgets.
import 'package:jaspr_test/jaspr_test.dart';
void main() {
testComponents('Test clicking the button increments the count', (tester) async {
tester.pumpComponent(MyComponent());
expect(find.text('Count: 0'), findsOneComponent);
await tester.click(find.tag('button'));
expect(find.text('Count: 1'), findsOneComponent);
});
}
testServer (Server-Side Rendering Testing)
Aimed at testing SSR applications. It directly starts a simulated local HTTP Server. You can send Requests and verify the HTML strings or state properties returned by the Server.
import 'package:jaspr_test/jaspr_test.dart';
void main() {
testServer('Verify Server-Side rendered HTML', (tester) async {
final response = await tester.request('/');
expect(response.document.body?.text, contains('Welcome to SSR'));
});
}
testClient (Browser Hydration and State Syncing Tests)
Runs in a headless browser environment, testing whether the client correctly takes over via hydration and handles DOM interactions after receiving initialStateData from the server.
Constraints
- Only use
AsyncStatelessComponenton the server side (jaspr/server.dart). Never in client UI code. - Do not nest Flutter
Widgets inside JasprComponents. - Choose the tester matching your render mode: SSR →
testServer, CSR →testComponents, browser →testClient. Prefer DOM string matching over strict typing.