# Building Jaspr Web Apps

> 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.

- Skill: `poorgramer-zack/building-jaspr-web-apps` (Agent Skill)
- Install (CLI): `npx skillmds@latest add poorgramer-zack/building-jaspr-web-apps`
- Raw SKILL.md: https://api.skillmd.com/api/skills/poorgramer-zack/building-jaspr-web-apps/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: Poorgramer-Zack (https://skillmd.com/u/poorgramer-zack)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/poorgramer-zack/building-jaspr-web-apps

---


# Jaspr Web Architecture Guide (v0.22.x)

## Goal
[Jaspr](https://jaspr.site/) 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`.

```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
```yaml
dev_dependencies:
  jaspr_tailwind: ^0.3.6
```

Create `web/styles.tw.css` with the Tailwind v4 CSS-first syntax:
```css
/* 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:

```dart
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
```yaml
dependencies:
  jaspr_router: ^0.8.2
```

```dart
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.

```dart
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`:

```dart
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
```bash
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`.
```dart
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.
```dart
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 `AsyncStatelessComponent` on the server side (`jaspr/server.dart`). Never in client UI code.
* Do not nest Flutter `Widget`s inside Jaspr `Component`s.
* Choose the tester matching your render mode: SSR → `testServer`, CSR → `testComponents`, browser → `testClient`. Prefer DOM string matching over strict typing.

