Skip to content

Status Page: Custom CSS/JS/HTML overrides #2863

@antonstrover

Description

@antonstrover

Is your feature request related to a problem? Please describe.

  • Teams need to brand and re-layout status pages without forking source; current approach makes upgrades hard.

Describe the solution you'd like

Add a Customisation section to Status Page settings (persisted in DB, survives restarts):

  1. Custom CSS textarea (fonts, colours, spacing, animations).
  2. Optional Custom HTML slots for header and footer; render these instead of defaults when present.
  3. Optional Custom JavaScript executed after page load (behind an explicit enable toggle/feature flag).
  4. Live preview before publish; export/import JSON for backup.
  5. Security: server-side HTML sanitisation, strict CSP, content size limits, audit log, and role-based access.
  6. Fix toggles: ensure charts and uptime % render when enabled; add unit/e2e tests.

Describe alternatives you’ve considered

  • Forking and patching source per deployment (works but painful to maintain).

  • Separate status-site fed by Checkmate webhooks + small DB + custom frontend.

  • Other open-source status pages for ideas or interim use:

    • Kener — minimal UI with a simple CSS override model.
    • OneUptime — supports custom HTML/CSS/JS; see technical notes below.

Additional context

Implementation from OneUptime in TypeScript

Schema: extend StatusPage with four text fields (PostgreSQL TEXT):

// StatusPage.ts
public headerHTML?: string;
public footerHTML?: string;
public customCSS?: string;
public customJavaScript?: string;

Guard with plan/role checks.

Admin UI: dedicated editors with syntax highlighting/validation:

  • HTML editors for header/footer
  • CSS editor
  • JavaScript editor

Editors integrate with a form schema for validation, syntax highlighting, and error reporting.

Rendering

  • CSS injection at runtime:
const css = statusPage.customCSS;
if (css) {
  const style = document.createElement('style');
  style.innerText = css;
  document.head.appendChild(style);
}
  • Conditional HTML replacement for components (example: header):
{!headerHtml ? (
  <Header logo={logo} links={links} onLogoClicked={...} />
) : (
  <div dangerouslySetInnerHTML={{ __html: headerHtml }} />
)}
  • Post-mount JS execution after the page finishes loading:
const onPageLoadComplete = () => {
  if (javascript) { new Function(javascript)(); }
};

Flow: server renders base template → client fetches status-page config → inject CSS → mount components with optional HTML overrides → run custom JS last.

State: custom content held in React state and updated from fetched config:

const [javascript, setJavaScript] = useState<string | null>(null);
// later
setJavaScript(get(masterpage, 'statusPage.customJavaScript') ?? null);

Ops/UX: preview without going live; works on both default and custom domains; premium-feature gating is possible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions