Pages

Pages are the foundation of Spark applications. Each page handles a route, loads data, and renders HTML on the server.

The @Page Annotation

Use the `@Page` annotation to define a route for your page.

@Page(path: '/users/:id')
class UserPage extends SparkPage<User> {
  // ...
}

Path parameters use the `:param` syntax. The `methods` parameter defaults to `['GET']` but can accept multiple methods.

The Generic Type

SparkPage uses a generic type `<T>` for type-safe data flow between `loader()` and `render()`. Use `void` for pages that don't load data.

// Page with data
class UserPage extends SparkPage<User> { ... }

// Page without data
class AboutPage extends SparkPage<void> { ... }

The loader() Method

The `loader()` method fetches data before rendering. It returns a `PageResponse<T>`.

@override
Future<PageResponse<User>> loader(PageRequest request) async {
  final id = request.pathParamInt('id');
  final user = await fetchUser(id);

  if (user == null) {
    return PageError.notFound('User not found');
  }

  return PageData(user);
}

PageResponse Types

The loader can return three types of responses:

// Success - pass data to render()
return PageData(user);

// Redirect to another page
return PageRedirect('/login');
return PageRedirect.permanent('/new-url');  // 301

// Error response
return PageError('Something went wrong');
return PageError.notFound();     // 404
return PageError.forbidden();    // 403
return PageError.badRequest();   // 400

The render() Method

The `render()` method generates HTML using the Element DSL. It receives the loaded data and returns an `Element`.

@override
Element render(User data, PageRequest request) {
  return div([
    h1([data.name]),
    p(['Email: ${data.email}']),
  ]);
}

PageRequest Helpers

Access request data using typed helpers:

// Path parameters
final id = request.pathParamInt('id');
final slug = request.pathParam('slug');

// Query parameters
final page = request.queryParamInt('page', 1);
final search = request.queryParam('q');

// Headers and cookies
final token = request.header('authorization');
final session = request.cookie('session_id');

Optional Overrides

Customize your page with these optional overrides:

// Page title
@override
String title(User data, PageRequest request) =>
    '${data.name} - Profile';

// Route middleware
@override
List<Middleware> get middleware => [authMiddleware()];

// Inline styles
@override
Stylesheet? get inlineStyles => css({ ... });

// Interactive components
@override
List<ComponentInfo> get components => [
  ComponentInfo('my-counter', Counter.new),
];