Frontend Pages and Resolvers
As explained in the Routes and Content documentation, we can use Mithril's routing system to show different components for different routes. Mithril allows you to use any component you like, even a Modal or Alert, but we recommend sticking to component classes that inherit the Page component.
The Page Component
We provide flarum/common/components/Page as a base class for pages in both the admin and forum frontends. It has a few benefits:
- Automatically updates
app.currentandapp.previousPageState when switching from one route to another. - Automatically closes the modal and drawer when switching from one route to another.
- Applies
this.bodyClass(if defined) to the '#app' HTML element when the page renders. - It's also good for consistency's sake to use a common base class for all pages.
- If the page's
scrollTopOnCreateattribute is set tofalseinoninit, the page won't be scrolled to the top when changed. - If the page's
useBrowserScrollRestorationis set tofalseinoninit, the browser's automatic scroll restoration won't be used on that page.
Page components work just like any other inherited component. For a (very simple) example:
import Page from 'flarum/common/components/Page';
export default class CustomPage extends Page {
view() {
return <p>Hello!</p>
}
}
Forum Page Structure
Flarum's forum frontend uses a generic page structure, which is defined in flarum/forum/components/PageStructure. This structure is used by all forum pages, and is recommended for use in extensions as well. You will have noticed that each forum page has a hero, sidebar, and content area among other things. These are all defined in PageStructure and can be used in your extension as well.
For example, a custom page component can use the PageStructure component as follows:
import PageStructure from 'flarum/forum/components/PageStructure';
export default class AcmePage extends Page {
view() {
return (
<PageStructure
className="AcmePage" // Optional but recommended.
hero={() => <CustomHero />} // Optional. Extends `flarum/forum/components/Hero`
sidebar={() => <div>Custom Sidebar</div>} // Optional.
loading={this.loading} // Optional.
>
<div>Custom Content</div>
</PageStructure>
);
}
}
PageStructure?Using PageStructure is not required, but it is recommended. It provides a consistent structure for all pages, and allows other extensions such as themes to extend and customize pages more easily.
Setting Page as Homepage
Flarum uses a setting to determine which page should be the homepage: this gives admins flexibility to customize their communities.
To add your custom page to the homepage options in Admin, you'll need to extend the BasicsPage.homePageItems method with your page's path.
An example from the Tags extension:
import { extend } from 'flarum/common/extend';
import BasicsPage from 'flarum/admin/components/BasicsPage';
export default function() {
extend(BasicsPage, 'homePageItems', items => {
items.add('tags', {
path: '/tags',
label: app.translator.trans('flarum-tags.admin.basics.tags_label')
});
});
}
To learn how to set up a path/route for your custom page, see the relevant documentation.
Page Titles
Often, you'll want some custom text to appear in the browser tab's title for your page. For instance, a tags page might want to show "Tags - FORUM NAME", or a discussion page might want to show the title of the discussion.
To do this, your page should include calls to app.setTitle() and app.setTitleCount() in its oncreate lifecycle hook (or when data is loaded, if it pulls in data from the API).
For example:
import Page from 'flarum/common/components/Page';
export default class CustomPage extends Page {
oncreate(vnode) {
super.oncreate(vnode);
app.setTitle("Cool Page");
app.setTitleCount(0);
}
view() {
// ...
}
}
export default class CustomPageLoadsData extends Page {
oninit(vnode) {
super.oninit(vnode);
app.store.find("users", 1).then(user => {
app.setTitle(user.displayName());
app.setTitleCount(0);
})
}
view() {
// ...
}
}
Please note that if your page is set as the homepage, app.setTitle() will clear the title for simplicity.
It should still be called though, to prevent titles from previous pages from carrying over.
PageState
Sometimes, we want to get information about the page we're currently on, or the page we've just come from.
To allow this, Flarum creates (and stores) instances of PageState as app.current and app.previous.
These store:
- The component class being used for the page
- A collection of data that each page sets about itself. The current route name is always included.
Data can be set to, and retrieved from, Page State using:
app.current.set(KEY, DATA);
app.current.get(KEY);
For example, this is how the Discussion Page makes its PostStreamState instance globally available.
You can also check the type and data of a page using PageState's matches method. For instance, if we want to know if we are currently on a discussion page:
import IndexPage from 'flarum/forum/components/IndexPage';
import DiscussionPage from 'flarum/forum/components/DiscussionPage';
// To just check page type
app.current.matches(DiscussionPage);
// To check page type and some data
app.current.matches(IndexPage, {routeName: 'following'});