MDN's frontend since late 2025, built with Web Components, Lit, and SSR for a fast, clean documentation experience. Fred = /fr(ont)e(n)d/.
See the codeMDN's next fr(ont)e(n)d.
.env-dist to .env and update values as needed. The file contains comments for guidance: cp .env-dist .env
npm installnpm run startnpm run start
NODE_ENV=production to run rari with the preview server, you'll need to have run npm run build firstnode --env-file=.env --run rari -- serve
npm run dev and npm run previewnpm run dev
npm run build
npm run preview to worknpm run preview
npm run test
See the l10n README.
If you want to access fred from a different machine, you'll need to run with certain options:
HTTPS=true to enable HTTPS with a self-signed certificate, allowing Web APIs requiring a secure context to workORIGIN_MAIN=your.local.ip.address to allowlist your address in the playgroundSo a full command might look like:
HTTPS=true ORIGIN_MAIN=192.168.0.99 npm run start
This is useful to test changes on mobile, tablets and other platforms.
tl;dr For visitors to MDN, we support the Baseline widely available browser set, with some minor modifications.
The Baseline widely available browser set is defined as browsers from the Core browser set whose initial release date is on or before 30 months prior to today's date, plus long-term support releases.
MDN supports these browsers, along with Firefox for iOS and all currently active Firefox ESR versions:
In this context, supported means that any issues with rendering or functionality are considered bugs and will be addressed as soon as reasonably possible.
For issues encountered while using unsupported browsers, we decide on a case-by-case assessment of whether the issue will be addressed; however, these issues may have lower priority. Issues with screen readers and other accessibility aids are likely to carry higher levels of importance.
We make our best efforts to design MDN to degrade gracefully; however, there are no guarantees of any level of functionality outside the supported browser set.
See the environment variables README.
We need to run some JS as soon as possible at page load, to avoid layout shifts and flashes.
We place this JS in entry.inline.js, and it's inlined on page load.
Rspack also generates the necessary CSP hash when doing a prod build with npm run build.
If this code is component-specific, it can be imported with ?source&csp=true and used to set the value of static inlineScript in a Server Component.
Remember to add an additional entry to the CSP hashes in yari when doing so.
We support a range of non-standard imports in our JavaScript. This includes:
?sourceImports the raw source of the file as a string.
import text from "./some-file.txt?source";
&csp=trueLogs a CSP hash for the source of the file during the production build.
Most commonly used alongside ?source to import the source of a file for inlining in a component, which needs to be allowlisted in our CSP:
import inlineScript from "./inline.js?source&csp=true";
See the layout README.
We have a basic sandbox for testing and styling components in isolation at http://localhost:3000/sandbox
To add a component to the sandbox, add a sandbox.js file to the component, which exports a class named like MyComponentSandbox which extends the SandboxComponent exported from components/sandbox/class.js.
components/ folder, with reserved names which cause certain behavior, explained further below:
component-name/
global.css - (reserved): automatically added to global styleselement.js - (reserved): custom element, automatically imported client side, always imported server sideelement.css - (recommended): styles for custom element's shadow domserver.js - (reserved): server component, will automatically load the adjacent server.css file when usedserver.css - (reserved): automatically added to page styles when its server component is used in that pageglobal.css: components which have CSS which should be loaded on all pages should expose that through a global.css file:
mdn-component-name { display: block; } or similarelement.js: custom elements should be defined in components/component-name/element.js
MDNComponentName
HTMLElement class names, and added to ACRONYMS in build/plugins/generate-element-map.js to allow the correct types to be generatedmdn-component-nameimport()types/element-map.d.ts to provide proper types in e.g. querySelector("mdn-component-name")server.js: server components should be defined in components/component-name/server.js
ServerComponent from components/server/index.js, and be named ComponentNameserver.css: server component styles should be placed in components/component-name/server.css
ServerComponent is used
// @ts-expect-error so we get an error when we fix the error and don't leave unnecessary // @ts-ignore comments lying around. While we're in active development these can lack a comment, but eventually we'll require an explanatory comment on each.If our server side rendered custom elements are different to the initial state of our custom elements when rendered client side, Lit will error out during hydration, stopping the execution of our client side JS.
To avoid this, don't compute things that are server/client dependent in connectedCallback (or run functions which do this). Instead you must run these in firstUpdated (despite the warning lit will raise in development about the element scheduling an update after an update completed).
This issue is tracked upstream: https://github.com/lit/lit/issues/1434
entry.ssr.js exports a top-level renderSimplified function: the purpose of this is to render a very basic HTML page for a particular path, which is useful for embedding MDN content as templated HTML in other contexts.
Any server component can define a renderSimplified method to define the simplified form of that component. When in the top-level renderSimplified context, any calls of ServerComponent.render() will automatically call the renderSimplified method of that component, falling back to the render method. This is so we can nest components with a renderSimplified method ("simplified components") within ones without.
Server components also have a simplifiedMode property set, which is true when rendered from the top-level renderSimplified function: this allows a small tweak within a render method without having to totally re-implement logic in the renderSimplified method.
There shouldn't be standalone simplified components: the nesting of components should be defined by the requirements of the render method. renderSimplified should only be added to a component which already exists with a render method to give a simplified view of it. This is especially important as, in the future, we may need to add options of what is/isn't rendered within renderSimplified for use in different contexts (one context may require a sidebar, another may not, for instance).
You can preview the rendering locally by setting FRED_SIMPLE_HTML:
FRED_SIMPLE_HTML=true npm run start
Then visit a documentation path directly, e.g. http://localhost:3000/en-US/docs/Web/
If you're loading a path which isn't rendering anything (like the homepage), check if it's defined in renderSimplified in entry.ssr.js: we "opt-in" routes as we need them.
See the testing README.
When a pull request is submitted, automated systems may be employed to run linting and test suites.
Our infrastructure includes the use of AI-assisted tooling. These systems function as automated review assistants, similar to enhanced linters. They may analyze changes and provide suggestions or feedback, but they do not have the ability to make decisions, approve changes, or modify pull requests.
AI-assisted linting/review is strictly limited to MDN’s engineering repositories (i.e. the code behind MDN), and is not applied to repositories that contain MDN’s content.
JavaScript
63.0%
CSS
27.1%
Fluent
8.9%
MDN's frontend since late 2025, built with Web Components, Lit, and SSR for a fast, clean documentation experience. Fred = /fr(ont)e(n)d/.
See the codeMDN's next fr(ont)e(n)d.
.env-dist to .env and update values as needed. The file contains comments for guidance: cp .env-dist .env
npm installnpm run startnpm run start
NODE_ENV=production to run rari with the preview server, you'll need to have run npm run build firstnode --env-file=.env --run rari -- serve
npm run dev and npm run previewnpm run dev
npm run build
npm run preview to worknpm run preview
npm run test
See the l10n README.
If you want to access fred from a different machine, you'll need to run with certain options:
HTTPS=true to enable HTTPS with a self-signed certificate, allowing Web APIs requiring a secure context to workORIGIN_MAIN=your.local.ip.address to allowlist your address in the playgroundSo a full command might look like:
HTTPS=true ORIGIN_MAIN=192.168.0.99 npm run start
This is useful to test changes on mobile, tablets and other platforms.
tl;dr For visitors to MDN, we support the Baseline widely available browser set, with some minor modifications.
The Baseline widely available browser set is defined as browsers from the Core browser set whose initial release date is on or before 30 months prior to today's date, plus long-term support releases.
MDN supports these browsers, along with Firefox for iOS and all currently active Firefox ESR versions:
In this context, supported means that any issues with rendering or functionality are considered bugs and will be addressed as soon as reasonably possible.
For issues encountered while using unsupported browsers, we decide on a case-by-case assessment of whether the issue will be addressed; however, these issues may have lower priority. Issues with screen readers and other accessibility aids are likely to carry higher levels of importance.
We make our best efforts to design MDN to degrade gracefully; however, there are no guarantees of any level of functionality outside the supported browser set.
See the environment variables README.
We need to run some JS as soon as possible at page load, to avoid layout shifts and flashes.
We place this JS in entry.inline.js, and it's inlined on page load.
Rspack also generates the necessary CSP hash when doing a prod build with npm run build.
If this code is component-specific, it can be imported with ?source&csp=true and used to set the value of static inlineScript in a Server Component.
Remember to add an additional entry to the CSP hashes in yari when doing so.
We support a range of non-standard imports in our JavaScript. This includes:
?sourceImports the raw source of the file as a string.
import text from "./some-file.txt?source";
&csp=trueLogs a CSP hash for the source of the file during the production build.
Most commonly used alongside ?source to import the source of a file for inlining in a component, which needs to be allowlisted in our CSP:
import inlineScript from "./inline.js?source&csp=true";
See the layout README.
We have a basic sandbox for testing and styling components in isolation at http://localhost:3000/sandbox
To add a component to the sandbox, add a sandbox.js file to the component, which exports a class named like MyComponentSandbox which extends the SandboxComponent exported from components/sandbox/class.js.
components/ folder, with reserved names which cause certain behavior, explained further below:
component-name/
global.css - (reserved): automatically added to global styleselement.js - (reserved): custom element, automatically imported client side, always imported server sideelement.css - (recommended): styles for custom element's shadow domserver.js - (reserved): server component, will automatically load the adjacent server.css file when usedserver.css - (reserved): automatically added to page styles when its server component is used in that pageglobal.css: components which have CSS which should be loaded on all pages should expose that through a global.css file:
mdn-component-name { display: block; } or similarelement.js: custom elements should be defined in components/component-name/element.js
MDNComponentName
HTMLElement class names, and added to ACRONYMS in build/plugins/generate-element-map.js to allow the correct types to be generatedmdn-component-nameimport()types/element-map.d.ts to provide proper types in e.g. querySelector("mdn-component-name")server.js: server components should be defined in components/component-name/server.js
ServerComponent from components/server/index.js, and be named ComponentNameserver.css: server component styles should be placed in components/component-name/server.css
ServerComponent is used
// @ts-expect-error so we get an error when we fix the error and don't leave unnecessary // @ts-ignore comments lying around. While we're in active development these can lack a comment, but eventually we'll require an explanatory comment on each.If our server side rendered custom elements are different to the initial state of our custom elements when rendered client side, Lit will error out during hydration, stopping the execution of our client side JS.
To avoid this, don't compute things that are server/client dependent in connectedCallback (or run functions which do this). Instead you must run these in firstUpdated (despite the warning lit will raise in development about the element scheduling an update after an update completed).
This issue is tracked upstream: https://github.com/lit/lit/issues/1434
entry.ssr.js exports a top-level renderSimplified function: the purpose of this is to render a very basic HTML page for a particular path, which is useful for embedding MDN content as templated HTML in other contexts.
Any server component can define a renderSimplified method to define the simplified form of that component. When in the top-level renderSimplified context, any calls of ServerComponent.render() will automatically call the renderSimplified method of that component, falling back to the render method. This is so we can nest components with a renderSimplified method ("simplified components") within ones without.
Server components also have a simplifiedMode property set, which is true when rendered from the top-level renderSimplified function: this allows a small tweak within a render method without having to totally re-implement logic in the renderSimplified method.
There shouldn't be standalone simplified components: the nesting of components should be defined by the requirements of the render method. renderSimplified should only be added to a component which already exists with a render method to give a simplified view of it. This is especially important as, in the future, we may need to add options of what is/isn't rendered within renderSimplified for use in different contexts (one context may require a sidebar, another may not, for instance).
You can preview the rendering locally by setting FRED_SIMPLE_HTML:
FRED_SIMPLE_HTML=true npm run start
Then visit a documentation path directly, e.g. http://localhost:3000/en-US/docs/Web/
If you're loading a path which isn't rendering anything (like the homepage), check if it's defined in renderSimplified in entry.ssr.js: we "opt-in" routes as we need them.
See the testing README.
When a pull request is submitted, automated systems may be employed to run linting and test suites.
Our infrastructure includes the use of AI-assisted tooling. These systems function as automated review assistants, similar to enhanced linters. They may analyze changes and provide suggestions or feedback, but they do not have the ability to make decisions, approve changes, or modify pull requests.
AI-assisted linting/review is strictly limited to MDN’s engineering repositories (i.e. the code behind MDN), and is not applied to repositories that contain MDN’s content.
JavaScript
63.0%
CSS
27.1%
Fluent
8.9%