Menu

Writing Pages

What is a page?

A page is a javascript representation of a hypertext document suitable for the world wide web and a web browser. Pages should roughly match one-to-one to urls for a web site. Pages have lifecycle methods that are called on them by react-server, which produce the html, either on the server or in the browser. By convention, pages are written as classes, but at their core, pages are Javascript objects with keys named for page lifecycle events, and which have corresponding functions that return elements, rendered as React instances.

The simplest of pages only needs a getElements method

export default class SimplePage {
    getElements () {
        return <h1>Hello react-server</h1>;
    }
}

Understanding the page lifecycle

Page lifecycle methods are called in groups that can return asynchronously in any order. We render the head element first

First, we call the following methods together

  • renderDebugComments
  • renderTitle
  • renderScripts
  • renderStylesheets

Once renderStylesheets has completed, we call the following methods together

  • renderMetaTags
  • renderLinkTags
  • renderBaseTag

All of which is then sent to the browser. Next, we call the rest of the page lifecycle is called in order

  1. getBodyClasses
  2. getBodyStartContent
  3. getElements

Once we reach the above the fold content, we'll start sending javascript.

If you don't provide a page lifecycle method, we provide a "best-guess" default value; for instance, you can omit the head methods altogether and get a performant and featureful page.

Examples

Full page

A react-server page that serves a full webpage.

import HttpStatus from 'http-status-codes';
import MobileEnabled from './middleware/MobileEnabled';
import ExampleComponent from './components/example-component';
import ExampleStore from './stores/example-store';
import exampleAction from './actions/example-action';

class ExamplePage {
    // See [writing middleware](/docs/writing-middleware) for how to write middleware
    static middleware() { return [MobileEnabled]; }

    handleRoute(next) {
        var params = this.getRequest().getQuery();
        this._exampleStore = new ExampleStore({
            id: +params.id
        });
        return next();
    }

    getTitle() {
        return "Example page"
    }

    getHeadStylesheets() {
        return [
            "/styles/example.css",
            "/styles/reset.css"
        ]
    }

    getMetaTags() {
        var tags = [
            { name: "example", content: "Demonstrate a full react-server page" },
        ];
        return tags;
    }

    getLinkTags() {
        return [
            // prefetch analytics to improve performance
            { rel: "prefetch", href: "//www.google-analytics.com" },
        ];
    }

    getBodyClasses() {
        return ["responsive-page", "typography"];
    }

    getElements() {
        return (
            <RootElement when={this._store.whenResolved()}>
                <h1>Example Page</h1>
                <ExampleComponent handleOnClick={exampleAction} {...this._exampleStore} />
            </RootElement>
        );
    }
}

Json endpoint

// returns a promise for example data
import getExampleData from './helpers/get-example-data';

export default class ExampleJsonPage {

    // see the example in [writing middleware](/docs/writing-middleware)
    static middleware() { return [JsonEndpoint] }

    handleRoute() {
        const id = this.getRequest().getRouteParams().id;
        this.data = getExampleData(id);
        return {code:200};
    }

    getResponseData() {
        return this.data;
    }
}

Setting Config values

For instance, to make a page into a fragment by setting the isFragment config value

import exampleComponent from './components/example-component';
import exampleStore from './stores/example-store';

export default class ExampleFragmentPage {
    setConfigValues() { return { isFragment: true }; }

    handleRoute() {
        this._store = exampleStore({ id: this.getRequest().getRouteParams().id });
        return {code:200};
    }

    getTitle () {
        return "School Fragment";
    }

    getElements() {
        return (
            <RootElement when={this._store.whenResolved()}>
                <h1>My example fragment page</h1>
                <exampleComponent />
            </RootElement>
        );
    }
}

export default ExamplePage;

Find out more

Check the page api to learn more. If you'd like to check the code, the page lifecycle is declared in renderMiddleware.js.