Page Layouts
Use layouts to create a consistent look and feel for your pages.
Page layouts define the overall structure and appearance of your pages. They allow you to wrap your page content with common elements like headers, footers, sidebars, and navigation menus, ensuring a consistent user experience across your site.jaspr_content
allows you to use pre-built layouts, create your own custom layouts, and easily switch between them.
How Layouts Work
When jaspr_content
renders a page, it takes the page's main content (often generated from Markdown or HTML) and passes it to a PageLayout
object. The layout is then responsible for embedding this content within a larger component tree.
You can specify a default layout for all pages in your ContentApp
configuration, or choose a specific layout for individual pages using frontmatter.
ContentApp(
// ...
layouts: [
// Default layout (first item)
DocsLayout(),
// Additional layouts
BlogLayout(),
MyCustomLayout(),
],
)
To use a specific layout for a page, you can specify it in the page's frontmatter:
---
title: My Awesome Page
layout: blog
---
This page will use `BlogLayout`.
The name is defined by the specific PageLayout
, like docs
for DocsLayout
or blog
for BlogLayout
. If no layout
is specified in the frontmatter, or if the specified layout is not found, the first registered layout in the ContentApp
's layouts
list is used as the default.
DocsLayout
The DocsLayout
is designed for documentation websites. It comes with:
- A header with a title, logo, and other actions.
- A sidebar for navigation between documentation sections.
- A table of contents for the current page.
BlogLayout
The BlogLayout
is tailored for blog posts and articles. It features:
- A header with a title, logo, and other actions.
- A prominent title block displaying the post's title, author, date, etc.
- Optional display of tags or categories.
EmptyLayout
The EmptyLayout
is a minimal layout that renders only the page's content without any additional surrounding elements like headers or footers. It's useful for pages where you want full control over the output or for content that will be embedded elsewhere.
Creating Custom Layouts
You can create your own layouts by implementing the PageLayout
interface or by extending the PageLayoutBase
class.
PageLayoutBase
provides a helpful starting point by handling common tasks like setting up the document's <head>
with meta tags.
Using PageLayoutBase
Here's an example of a simple custom layout:
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_content/jaspr_content.dart';
class MyCustomLayout extends PageLayoutBase {
const MyCustomLayout();
@override
Pattern get name => 'my_custom_layout_name'; // To be used in frontmatter.
@override
Iterable<Component> buildHead(Page page) sync* {
// Add common meta tags.
yield* super.buildHead(page);
yield Style(styles: [
css('.custom_layout', [
// Add custom css rules for this layout.
]),
]);
}
@override
Component buildBody(Page page, Component child) {
// `page` contains all page data, including frontmatter.
// `child` is the rendered content of the page.
return div(classes: 'custom_layout', [
header([
// Add a custom header.
]),
main_([
child,
]),
footer([
// Add a custom footer.
]),
]);
}
}
Then, register your custom layout in ContentApp
:
ContentApp(
// ...
layouts: [
MyCustomLayout(),
// other layouts
],
)
Adding a Table of Contents
The TableOfContentsExtension
generates a table of contents based on the headings in your page. You can then display this in your layout.
First, ensure the extension is added to your ContentApp
:
ContentApp(
// ...
extensions: [
TableOfContentsExtension(),
// other extensions
],
// ...
)
The extension stores the TableOfContents
object in page.data['toc']
. Your custom layout can then access and render it:
// ...
@override
Component buildBody(Page page, Component child) {
// ...
return div([
// ...
main_([
child,
aside(classes: 'toc', [
if (page.data['toc'] case TableOfContents toc)
div([
h3([text('On this page')]),
toc.build(), // Renders the ToC as an unstyled <ul> list
]),
]),
]),
// ...
]);
}
// ...
Using Layout Components
jaspr_content
provides several additional components that are commonly used within layouts to add functionality and structure. You can use these components both with existing layouts or your own custom implementations.
Header
A Header
component is often used at the top of the page. It can contain the site title, logo, navigation links, and other elements like a theme toggle. Both DocsLayout
and BlogLayout
accept a header
parameter.
Example usage with BlogLayout
:
BlogLayout(
header: Header(
title: 'My Site',
logo: '/assets/logo.png',
items: [
Link(text: 'Home', href: '/'),
Link(text: 'Articles', href: '/articles'),
ThemeToggle(), // Described below
GithubButton(repo: '<user>/<repo>'), // Described below
],
)
)
Sidebar
A Sidebar
is common in documentation sites for navigating through different pages or sections. DocsLayout
accepts a sidebar
parameter. The Sidebar
typically contains SidebarGroup
s and SidebarLink
s.
Example usage with DocsLayout
:
DocsLayout(
sidebar: Sidebar(groups: [
SidebarGroup(
links: [
SidebarLink(text: "📖 Overview", href: '/'),
SidebarLink(text: "🧭 About", href: '/about'),
],
),
SidebarGroup(title: 'Get Started', links: [
SidebarLink(text: "🚀 Quick Start", href: '/get_started/quick_start'),
]),
]),
),
The DocsLayout
also adds a menu button in the Header
to show/hide the sidebar on smaller screens. When you are not using the Header
component you can add this menu button manually like this:
div(attributes: {'data-has-sidebar': ''}, [
// ...,
header([
SidebarToggleButton(),
// Other header elements ...
]),
// ...
])
GithubButton
The GithubButton
component can be used to display a link to a GitHub repository including its number of stars and forks.
GithubButton(repo: '<user>/<repo>')
ThemeToggle
The ThemeToggle
component allows users to switch between light and dark mode for the site. This component works in conjunction with the ContentTheme
defined in your ContentApp
. Learn more about Theming
ThemeToggle()