Skip to main content

Overview

Document is a special component for managing document-level elements like <html>, <head>, <body>, and their contents. It provides different factory constructors for various document manipulation scenarios.

Factory Constructors

Document() - Base Document

const Document({
  String? title,
  String? lang,
  String? base,
  String? charset,
  String? viewport,
  Map<String, String> meta = const {},
  List<StyleRule> styles = const [],
  List<Component> head = const [],
  required Component body,
})
Sets up a basic document structure at the root of your app and renders the main <html>, <head> and <body> tags.
title
String?
Rendered as the <title> element in <head>.
lang
String?
Set as the lang attribute on the <html> element.
base
String?
default:"'/'"
Rendered as the <base href="..."> element. Automatically normalized to start and end with /.
charset
String?
default:"'utf-8'"
Rendered as a <meta charset="..."> element in <head>.
viewport
String?
default:"'width=device-width, initial-scale=1.0'"
Rendered as a <meta name="viewport" content="..."> element.
meta
Map<String, String>
default:"{}"
Additional meta tags rendered as <meta name="key" content="value"> elements.
styles
List<StyleRule>
default:"[]"
CSS styles rendered in a <style> element inside <head>.
head
List<Component>
default:"[]"
Additional components rendered inside <head>.
body
Component
required
The component rendered inside the <body> element.
Example:
Document(
  title: 'My App',
  lang: 'en',
  meta: {
    'description': 'A Jaspr web application',
    'keywords': 'jaspr, web, dart',
  },
  styles: [
    css('body', [
      Css.background(color: Colors.white),
    ]),
  ],
  body: App(),
)

Document.template() - Template Document

const Document.template({
  String name = 'index',
  String attachTo = 'body',
  required Component child,
})
Loads an external .template.html file from the filesystem and attaches the provided child component to that template.
name
String
default:"'index'"
Defines which template file to load: web/<name>.template.html.
attachTo
String
default:"'body'"
CSS selector defining where to attach the child component in the loaded template.
child
Component
required
The component to attach to the template.
Example:
// Loads web/index.template.html and attaches App() to <body>
Document.template(
  child: App(),
)

// Loads web/custom.template.html and attaches to #app element
Document.template(
  name: 'custom',
  attachTo: '#app',
  child: App(),
)

Document.html() - HTML Attributes

const Document.html({
  Map<String, String>? attributes,
  Key? key,
})
Attaches a set of attributes to the <html> element. This can be used at any point in the component tree and is supported both on the server during pre-rendering and on the client.
attributes
Map<String, String>?
Attributes to attach to the <html> element.
key
Key?
An optional key for the component.
Can be used multiple times. Deeper or latter mounted components will override duplicate attributes from other Document.html() components.
Example:
Document.html(
  attributes: {
    'data-theme': 'dark',
    'class': 'app-root',
  },
)

Document.head() - Head Elements

const Document.head({
  String? title,
  Map<String, String>? meta,
  List<Component>? children,
  Key? key,
})
Renders metadata and other elements inside the <head> of the document. Any children are pulled out of the normal rendering tree and rendered inside a special section of the <head> element. This is supported both on the server during pre-rendering and on the client.
title
String?
Rendered as the <title> element.
meta
Map<String, String>?
Meta tags rendered as <meta name="key" content="value"> elements.
children
List<Component>?
Additional components rendered inside <head>.
key
Key?
An optional key for the component.
Override Behavior: Elements rendered by nested Document.head() are overridden using this system:
  • Elements with an id override other elements with the same id
  • <title> and <base> elements override other <title> or <base> elements
  • <meta> elements override other <meta> elements with the same name
Can be used multiple times. Deeper or latter mounted components will override duplicate elements from other Document.head() components.
Example:
// Parent component
Parent(
  children: [
    Document.head(
      title: 'My Title',
      meta: {'description': 'My Page Description'},
    ),
    Child(
      children: [
        Document.head(
          title: 'Nested Title',
        ),
      ],
    ),
  ],
)

// Results in:
// <head>
//   <title>Nested Title</title>
//   <meta name="description" content="My Page Description">
// </head>

Document.body() - Body Attributes

const Document.body({
  Map<String, String>? attributes,
  Key? key,
})
Attaches a set of attributes to the <body> element. This can be used at any point in the component tree and is supported both on the server during pre-rendering and on the client.
attributes
Map<String, String>?
Attributes to attach to the <body> element.
key
Key?
An optional key for the component.
Can be used multiple times. Deeper or latter mounted components will override duplicate attributes from other Document.body() components.
Example:
Document.body(
  attributes: {
    'class': 'no-scroll',
    'data-page': 'home',
  },
)

Common Use Cases

Basic App Setup

import 'package:jaspr/jaspr.dart';

class App extends StatelessComponent {
  @override
  Component build(BuildContext context) {
    return Document(
      title: 'My Jaspr App',
      lang: 'en',
      meta: {
        'description': 'A modern web app built with Jaspr',
        'author': 'Your Name',
      },
      body: HomePage(),
    );
  }
}

Dynamic Page Titles

class BlogPost extends StatelessComponent {
  const BlogPost({required this.post, super.key});

  final Post post;

  @override
  Component build(BuildContext context) {
    return div([
      Document.head(
        title: post.title,
        meta: {
          'description': post.excerpt,
          'author': post.author,
        },
      ),
      article([
        h1([text(post.title)]),
        p([text(post.content)]),
      ]),
    ]);
  }
}

SEO Metadata

class ProductPage extends StatelessComponent {
  const ProductPage({required this.product, super.key});

  final Product product;

  @override
  Component build(BuildContext context) {
    return div([
      Document.head(
        title: '${product.name} - Buy Now',
        meta: {
          'description': product.description,
          'keywords': product.tags.join(', '),
        },
        children: [
          // Open Graph tags
          meta(name: 'og:title', content: product.name),
          meta(name: 'og:description', content: product.description),
          meta(name: 'og:image', content: product.imageUrl),
          meta(name: 'og:type', content: 'product'),
          // Twitter Card tags
          meta(name: 'twitter:card', content: 'summary_large_image'),
          meta(name: 'twitter:title', content: product.name),
        ],
      ),
      ProductDetails(product: product),
    ]);
  }
}

Dark Mode Toggle

class ThemeToggle extends StatefulComponent {
  @override
  State createState() => _ThemeToggleState();
}

class _ThemeToggleState extends State<ThemeToggle> {
  bool isDark = false;

  @override
  Component build(BuildContext context) {
    return div([
      Document.html(
        attributes: {'data-theme': isDark ? 'dark' : 'light'},
      ),
      button(
        events: {
          'click': (_) => setState(() => isDark = !isDark),
        },
        [text('Toggle Theme')],
      ),
    ]);
  }
}

External Stylesheets and Scripts

Document(
  title: 'App with External Resources',
  head: [
    // External stylesheet
    link(rel: 'stylesheet', href: '/styles/theme.css'),
    // Google Fonts
    link(
      rel: 'stylesheet',
      href: 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;700',
    ),
    // External script
    script(src: '/scripts/analytics.js', defer: true, []),
    // Favicon
    link(rel: 'icon', type: 'image/png', href: '/favicon.png'),
  ],
  body: App(),
)

Using Custom Template

<!-- web/index.template.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="/styles/global.css">
  </head>
  <body>
    <div id="app">
      <!-- Jaspr will attach here -->
    </div>
    <script src="/scripts/polyfills.js"></script>
  </body>
</html>
Document.template(
  attachTo: '#app',
  child: App(),
)

Platform Differences

The base Document() constructor and Document.template() are only available on the server. On the client, only Document.html(), Document.head(), and Document.body() are available.

Important Notes

Only one root Document component (using the base constructor or template constructor) should exist in your application. Using multiple will result in an error.
The Document.head(), Document.html(), and Document.body() factory constructors can be used anywhere in the component tree, including within child components deep in the hierarchy.

Build docs developers (and LLMs) love