Skip to content

Conversation

@LeoMcA
Copy link
Member

@LeoMcA LeoMcA commented Nov 11, 2025

Description

  • Adds a renderSimple method to the server component class, which can be overwritten in a component to define a simplified HTML version of the component
  • Does this for the Doc component to render simply the page contents, no sidebars etc.
  • Doesn't render the outer layout either

Motivation

  • Main motivation is to provide a better API than the hacky one used in feat: add get-doc tool mcp#10
    • With the renderSimple export in entry.ssr.js I don't have to set up async local storage, mock fluent, etc.
    • Will also easily allow us to define simple forms of other components, e.g. Baseline or BCD, rather than maintaining a custom implementation divorced from the "real" component
  • This could also prove useful for other purposes we might want to explore in the future: an alternative offline option, embedding in devtools, etc.

Testing

For a preview of the simple form of a component, run:

FRED_SIMPLE_HTML=true npm start

And then navigate directly to a doc page (other page types will render nothing), e.g. http://localhost:3000/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger

Screenshot

Screen Shot 2025-11-11 at 17 17 58

@LeoMcA LeoMcA requested a review from a team as a code owner November 11, 2025 17:19
@LeoMcA LeoMcA requested a review from caugner November 11, 2025 17:19
@github-actions
Copy link
Contributor

github-actions bot commented Nov 11, 2025

b65d50c was deployed to: https://fred-pr1072.review.mdn.allizom.net/

Copy link
Contributor

@caugner caugner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we avoid renderSimple() if an environment variable just caused this effect?

render(context, markup) {
const asyncStore = asyncLocalStorage.getStore();
if (!asyncStore) {
if (!asyncStore || "renderSimple" in asyncStore) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would renderSimple end up in asyncStore?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't in the outer layout, since we don't call it from the renderSimple root function: this is simply to make typescript happy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize that I don't fully understand what asyncLocalStorage holds.

I looked at https://github.com/mdn/fred/blob/22628366e1b9ecc0d8f36811492c5a6f08e62db4/components/server/async-local-storage.js and it doesn't have any information, it just creates an instance of AsyncLocalStorage without any semantic notion of what it contains.

It is not clear to me why "renderSimple" in asyncStore implies that asyncLocalStorage is missing. It would probably be better to throw a separate error in this case, because if asyncStore is truthy, it's apparently not missing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it's a little bit esoteric: the top level render methods (in entry.ssr.js) populate it. I'll add a jsdoc comment or similar to explain what's going on, and point back to there.

Agreed on the error, that was just me being a little lazy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment, and realised the name of AsyncLocalStorageContents may have seemed a little "official" - like it was coming from node itself - rather than something we had defined so renamed it, in 198df40 (#1072)

Copy link
Member Author

@LeoMcA LeoMcA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we avoid renderSimple() if an environment variable just caused this effect?

Hmm, let me think about this: I think though it would make the component code more complex, and I think there's a nice simplicity to just having two render methods on components which need them.

render(context, markup) {
const asyncStore = asyncLocalStorage.getStore();
if (!asyncStore) {
if (!asyncStore || "renderSimple" in asyncStore) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wouldn't in the outer layout, since we don't call it from the renderSimple root function: this is simply to make typescript happy.

Copy link
Contributor

@caugner caugner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, just three more nits:

  1. Should the method be called renderSimplified() or renderMinimal() to make it clearer what this method does.
  2. Can we mention this in the docs (README?).
  3. Could every renderSimple() implementation incl. default implementation explain in JSDoc what it omits?

Comment on lines +84 to +87
/**
* @param {...any} args
* @returns {any}
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add description of this method

Comment on lines +14 to +16
/**
* @param {import("@fred").Context<import("@rari").DocPage>} context
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: explain what this method omits (even if it's comparably trivial from looking at render/renderSimple side-by-side

Comment on lines +48 to +50
/**
* @param {import("@fred").Context<import("@rari").DocPage>} context
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: explain what this simplified/minimal rendering omits

@caugner caugner changed the title feat: components can render to simplified html feat(ServerComponent): add method to render simplified html Nov 21, 2025
Comment on lines +4 to +7
* Store for internal context passed around components.
*
* Generally only used within the `ServerComponent` class itself,
* or very special server components (such as the `OuterLayout`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Can you give an example?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants