HTML Template

Modern.js provides JSX syntax and HTML(EJS) syntax to customize the HTML template.

JSX Syntax

According to Modern.js conventions, you can create a Document.[jt]sx file under src/ or the entry directory and default export a component". The rendering result of this component can be used as the HTML template of the entry.

For example, consider the following directory structure:

.
└── src
    ├── Document.tsx
    ├── entry-a
    │   ├── Document.tsx
    │   └── routes
    ├── entry-b
    │    └── routes
    └── modern-app-env.d.ts

entry-a will use the Docoument.[jt]sx file under the current entry as the template. If there is no Document.[jt]sx file under the current entry, like entry-b, it will look for the Document.[jt]sx file under the root directory.

If not found, default template will be used.

HTML Components

Modern.js provides some components for rendering pages to help developers generate templates. These components can be used from @modern-js/runtime/document:

import { Html, Body, Root, Head, Scripts } from '@modern-js/runtime/document';
  • Html: provide the ability of native HTML Element and and render necessary components that the developer did not add by default. <Head> and <Body> must exist, and other components can be assembled as needed.

  • Body: provide the ability of native Body Element and needs to contain the <Root> component internally. It also supports other elements as child elements at the same time, such as adding footers.

  • Root: the root node <div id='root'></div> to be rendered. The default id of the root node is id = 'root'. You can set props.rootId to change the id attribute. Child components can be added and will be rendered in the HTML template. After React rendering is complete, it will be overwritten and is generally used to implement global Loading.

  • Head: provide the ability of native Head Element and automatically fills in <meta> and <Scripts> components.

  • Scripts: Used to control the placement of the <script> tags generated by the build. By default, they are placed within the <Head> component.

  • Comment: retain user-written comments like <!-- gateway --> and outputs them to the rendered HTML.

Template Parameters

Because it is in JSX format, various variables can be used freely in the component to assign values to various custom components in Document.[jt]sx.

Modern.js also provides DocumentContext to provide some configuration and environment parameters for easy access. The main parameters are:

  • processEnv: provides the process.env during the build.
  • config: the configuration of the Modern.js project. Currently, only the output configuration is exposed.
  • entryName: the current entry name.
  • templateParams: parameters of HTML template (compatible with traditional templates, so it's not recommended for use).

Basic Example

import React, { useContext } from 'react';
import {
  Html,
  Root,
  Head,
  Body,
  Comment,
  DocumentContext,
} from '@modern-js/runtime/document';

export default function Document(): React.ReactElement {
  // the params provide by DocumentContext
  const {
    config: { output: htmlConfig },
    entryName,
    templateParams,
  } = useContext(DocumentContext);

  return (
    <Html>
      <Head>
        <link href="https://modernjs.dev" />
        <Comment>{'<!-- Need a Comment -->'}</Comment>
      </Head>
      <Body>
        <Root rootId="root">
          <h1 style={{ color: 'red' }}>Some Params: </h1>
          <h2>entryName: {entryName}</h2>
          <h2>title: {htmlConfig.title}</h2>
          <h2>rootId: {templateParams.mountId}</h2>
        </Root>
        <h1>bottom</h1>
      </Body>
    </Html>
  );
}

The above JSX components will generate the following HTML template:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, shrink-to-fit=no, viewport-fit=cover, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />
    <meta http-equiv="x-ua-compatible" content="ie=edge" />
    <meta name="renderer" content="webkit" />
    <meta name="layoutmode" content="standard" />
    <meta name="imagemode" content="force" />
    <meta name="wap-font-scale" content="no" />
    <meta name="format-detection" content="telephone=no" />
    <link rel="icon" href="/a.icon" />
    <script defer src="/static/js/builder-runtime.js"></script>
    <script defer src="/static/js/lib-react.js"></script>
    <script defer src="/static/js/lib-polyfill.js"></script>
    <script defer src="/static/js/lib-router.js"></script>
    <script defer src="/static/js/main.js"></script>
    <link href="https://modernjs.dev" />
    <!-- Need a Comment -->
  </head>

  <body>
    <div id="root">
      <!--<?- html ?>-->
      <h1 style="color:red">Some Params:</h1>
      <h2>entryName: main</h2>
      <h2>title:</h2>
      <h2>rootId: root</h2>
    </div>
    <h1>bottom</h1>
    <!--<?- chunksMap.js ?>-->
    <!--<?- SSRDataScript ?>-->
  </body>
</html>

Scripts Component Example

You can use the <Scripts> component to insert the <script> tags generated by the build inside the <body> tag:

import React from 'react';
import { Html, Root, Head, Body, Scripts } from '@modern-js/runtime/document';

export default function Document(): React.ReactElement {
  return (
    <Html>
      <Head></Head>
      <Body>
        <Root rootId="root"></Root>
        <Scripts />
      </Body>
    </Html>
  );
}

HTML Syntax

Modern.js also supports generating HTML files using HTML (EJS) syntax.

By default, Modern.js projects come with a built-in HTML template for generating HTML code. If you need to customize the HTML template, you can use two methods: Custom HTML Fragments and Fully Custom HTML Templates.

Custom HTML Fragments

Under the application root directory, create the config/html/ directory, which supports the creation of four types of HTML fragments:

  • top.html
  • head.html
  • body.html
  • bottom.html

These fragments will be injected into the default HTML template according to their positions.

<!DOCTYPE html>
<html>
  <head>
    <%= topTemplate %>
    <%= headTemplate %>
    {/* webpack inject css  */}
  </head>
  <body>
    <noscript>
      We're sorry but react app doesn't work properly without JavaScript
      enabled. Please enable it to continue.
    </noscript>
    <div id="<%= mountId %>"></div>
    <%= bodyTemplate %>
    {/* webpack inject js  */}
    {/* <?- bottomTemplate ?> */}
  </body>
</html>

HTML fragments support the use of Lodash template.

For example, to insert an external script in body.html:

config/html/body.html
<script src="//example.com/assets/a.js"></script>
INFO

The implementation of custom HTML fragments is to merge the fragments with the built-in template. Since the <title> already exists in the default template, the title tag in the custom HTML template will not take effect. Please use html.title to modify the page title.

Custom the entire HTML Template

In some cases, HTML fragments may be is not better meet the customization requirements. Modern.js provides a fully customized way.

Note

It is generally not recommended to directly override the default HTML template, as some functional options may be lost. If it is truly necessary to customize the entire HTML template, it is recommended to modify based on the built-in template as needed.

Under the config/html/ directory, create an index.html file that will replace the default HTML template.

INFO

The default HTML template can be viewed in node_modules/.modern-js/${entryName}/index.html.

Template Parameters

The parameters used in the template can be defined by the html.templateParameters configuration.

Config By Entry

The HTML fragments in the config/html/ directory take effect for all entries in the application. If you want to customize HTML fragments by entry, you can create a directory named after the entry name under the config/html/ directory, and then customize the HTML fragments in this directory.

For example, the following HTML fragments are only effective for the entry1 entry:

.
├── config/
│   └── html/
│       └── entry1
│           ├── head.html
│           └── body.html
└── src/
    ├── entry1/
    │   └── routes
    └── entry2/
        └── routes