Skip to content

Custom Pages ​

A custom page lets you add your own page to the CMS, next to the Contents, Assets and Editorial Workflow pages. It can show a dashboard built with a React component, embed another web page, such as your analytics or deployment status, or simply link to an external site.

Overview ​

To register a custom page, use the registerCustomPage method on the CMS object:

js
CMS.registerCustomPage(definition);

Parameters ​

  • definition (object, required): The page definition, with the following properties:
    • id (string, required): A unique identifier for the page, consisting of alphanumeric characters, hyphens and underscores. The page is opened at #/pages/{id}, so you can link to it directly. Registering a page with the same ID again replaces the previous one in place.
    • label (string, required): The label of the page, displayed as the page title and as the tooltip of the page’s button.
    • icon (string, optional): The name of a Material Symbols icon displayed on the page’s button, e.g. monitoring. Defaults to extension.
    • component (React component): A React component rendering the page content. The component receives no props. You can write it with HTM, JSX or h() calls.
    • url (string): The URL of a web page to show. A relative URL is resolved against the current page URL. Only http and https URLs are supported.
    • newTab (boolean, optional): Whether to open the url in a new browser tab instead of embedding it in the CMS. Defaults to false. Can only be used with url.

Either component or url must be given, but not both. An invalid definition throws a TypeError.

Where Pages Appear ​

On desktop, custom pages are listed in the global toolbar after the built-in pages, in the order of registration, and can be opened with the Alt+1–9 keyboard shortcuts along with the built-in pages. A page opening in a new tab is shown as a separate button after the page buttons.

On mobile, the bottom navigation has little room, so custom pages are listed in the Pages section of the Menu page instead.

Pages can be registered before or after the CMS is initialized. A page registered later appears right away, and a link to #/pages/{id} shows the page as soon as it’s registered.

Examples ​

React Component ​

The following page greets the user and counts their clicks, using the useState hook. A component can use any hook taken from CMS.React.

js
const { useState } = CMS.React;

const Dashboard = () => {
  const [count, setCount] = useState(0);

  return html`
    <div style="padding: 16px">
      <h3>Welcome back!</h3>
      <p>You’ve clicked the button ${count} times.</p>
      <button type="button" onClick=${() => setCount(count + 1)}>Click Me</button>
    </div>
  `;
};

CMS.registerCustomPage({
  id: 'dashboard',
  label: 'Dashboard',
  icon: 'dashboard',
  component: Dashboard,
});
jsx
const { useState } = CMS.React;

const Dashboard = () => {
  const [count, setCount] = useState(0);

  return (
    <div style={{ padding: 16 }}>
      <h3>Welcome back!</h3>
      <p>You’ve clicked the button {count} times.</p>
      <button type="button" onClick={() => setCount(count + 1)}>
        Click Me
      </button>
    </div>
  );
};

CMS.registerCustomPage({
  id: 'dashboard',
  label: 'Dashboard',
  icon: 'dashboard',
  component: Dashboard,
});
js
const { useState } = CMS.React;

const Dashboard = () => {
  const [count, setCount] = useState(0);

  return h(
    'div',
    { style: { padding: 16 } },
    h('h3', null, 'Welcome back!'),
    h('p', null, `You’ve clicked the button ${count} times.`),
    h('button', { type: 'button', onClick: () => setCount(count + 1) }, 'Click Me'),
  );
};

CMS.registerCustomPage({
  id: 'dashboard',
  label: 'Dashboard',
  icon: 'dashboard',
  component: Dashboard,
});

The page area has no padding, so your component can fill it, e.g. with a full-size chart. Add your own padding as needed.

Embedded Web Page ​

To show another web page within the CMS, provide its URL. The page is embedded in an iframe that fills the page area.

js
CMS.registerCustomPage({
  id: 'analytics',
  label: 'Analytics',
  icon: 'monitoring',
  url: 'https://plausible.io/share/example.com?auth=XXXXXXXX',
});

A relative URL is resolved against the CMS admin page URL, so a page of your own site can be embedded as well:

js
CMS.registerCustomPage({
  id: 'style-guide',
  label: 'Style Guide',
  icon: 'palette',
  url: '/style-guide/',
});

Embedding restrictions

Many sites prevent their pages from being embedded in an iframe with the X-Frame-Options or Content-Security-Policy response header. Such a page appears blank or shows an error in the CMS. Use the newTab option for these sites instead.

Security note

A page embedded from the same origin as the CMS, e.g. a page on your own site when the CMS is served from the same domain, has full access to the CMS, including the signed-in user’s access token stored in the browser. Only embed pages you trust.

To add a shortcut to an external site, such as your hosting dashboard or a style guide, set the newTab option to true. The URL is opened in a new browser tab, and the user stays where they are in the CMS.

js
CMS.registerCustomPage({
  id: 'netlify',
  label: 'Netlify Dashboard',
  icon: 'cloud',
  url: 'https://app.netlify.com/sites/example/overview',
  newTab: true,
});

Migrating from Static CMS ​

Static CMS provided a similar feature with the CMS.registerAdditionalLink() method. As Static CMS has been archived, Sveltia CMS doesn’t support the method. Use CMS.registerCustomPage() instead:

  • id and title correspond to id and label, respectively.
  • data corresponds to component for a React component, or url for a link. Add newTab: true to open the link in a new tab, or omit it to embed the page in the CMS.
  • options.iconName corresponds to icon, which takes a Material Symbols icon name rather than the name of an icon registered with CMS.registerIcon().