frontend thoughts kayoh.dev
Sharing Internal Frontend Components with a Docs Site
01The problem with parallel copies
Updating documentation becomes harder when its content and examples
are maintained separately from the product they describe. Picture a
product monorepo, acme-app, shipping a
ProductCard from an internal package; a docs site wants
to show that card.
Copy the component into docs, or build a separate example, and you take on a second implementation to keep in sync. Publish the component so docs can install it and every tweak becomes a publish-and-bump cycle. Those extra steps make it easier to put off the docs update.
02Content beside the application
When a product's documentation sits in the product's monorepo, a code
change and the prose explaining it can be authored in one commit and
reviewed as one diff. Whoever changes ProductCard is best
placed to fix the sentence about it.
That does not mean the docs site belongs there. Documentation content lives in each product's monorepo, and a separate docs repository brings it together and renders its component examples.
The docs repository checks out a selected revision of the product as a submodule and links to the product's internal package by path, so it can import it without a registry release. Documentation content and component source come from that same product revision.
03Render the real component
Rendering the actual ProductCard means one source file
serves both the app and the docs, so there is no second UI to keep in
step. A fixture supplies the props, letting docs show deliberate
states - empty, loading, an error - without production data.
Rendering the real component and fetching production data are separate choices: the component removes the duplicate implementation, while the data can be whatever the page needs. Live data can be useful for a catalogue or status page; sample data is usually enough for an example.
When examples are checked against the component's types, type-checking can catch incompatible fixture data.
04Internal is not a reason to publish
A shared internal component often invites a promotion: move it into the published design system so any app, docs included, can import it.
A published package can be product-specific. What publishing adds is a
release step: versioning, a registry, and a publish-and-bump cycle for
every change. A workspace-internal package lives inside one monorepo,
depends on that product's siblings, changes quickly and has no publish
step. ProductCard is the second kind.
Consuming the internal package directly lets documentation reuse the component without adding a release process just for that purpose.
05What the docs build takes on
Consuming internal source moves work into the docs build. Docs has to supply dependencies compatible with the product's, the styles the component expects the app to provide, and any runtime context - providers, routing - it needs.
A product change reaches the docs site when its pinned revision is updated and the site rebuilds. That still requires an update, but the content and components move together without a separate package release. Advancing the pin is a change in the docs repository, which records the new product revision.
Rendering the component in docs reveals dependencies that were implicit inside the app: a broad import surface, styling tokens that were always present, providers that were always mounted. Docs must now name and supply each one.
It pays off when products share a frontend stack, the components change often, docs needs the real UI, and you can absorb the cross-repo wiring. I would stop when those are missing: no shared package, a mismatched docs stack, or a component so tied to its host that reusing it means bringing the host along.
06What improves
There is less to maintain, and the content can change in the same pull request as the product. One implementation serves both the app and the docs.
The prose still needs a person. A real component renders perfectly under an explanation that is years out of date, and someone still has to notice and fix that.
The goal is to make updating documentation a smaller, more natural part of changing the product.
07Related reading
I use Fumadocs and pnpm for this setup.
- Fumadocs - the framework used to build the docs site.
- pnpm workspaces - background on sharing internal packages within a workspace.