feat: keep "use client" off the builder so RSC can call it - #156
Merged
Conversation
The banner was applied to the whole `src/index.ts` bundle, making every export a client module. `builder()` from a server component threw `Attempted to call builder() from the server`, which left generating the CSS at build time to the CLI. The React surface moves to its own `src/react.ts` bundle, which is where the banner lands; `dist/index.js` keeps `./react.js` external and re-exports it, so esbuild leaves the directive alone instead of inlining the module. The public API is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
abernier
force-pushed
the
feat/server-safe-builder
branch
from
July 31, 2026 11:26
30b62ee to
067d655
Compare
This was referenced Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #155.
dist/index.jscarries a"use client"banner over the whole bundle, so every export is a client module —builderincluded. Calling it from a React Server Component throws:Which means an app that wants the CSS in its server-rendered HTML can't just do
builder(source).toCss()in a layout — it has to shell out to the CLI in a prebuild step. The banner is there for<Mcu>, andbuilderis caught in the blast radius.The change
src/react.tsbecomes the React entry —Mcu,useMcu,ExportButton— and is built as its own bundle, which is the only one that gets the banner.src/index.tskeeps./react.jsexternal so esbuild leaves the import alone instead of inlining the module and dropping its directive.The public API doesn't move.
builder,Mcu,useMcuandExportButtonare still all imported frommaterial-theme-builder; no new subpath to learn, nothing to migrate. ImportingMcuinto a server component keeps working too —index.jsre-exports a module that carries the directive, so the framework resolves it to a client reference exactly as before.Verified against a real Next build
Minimal App Router app,
output: "export",builder()called at module scope inapp/layout.tsx— i.e. a server component — and<Mcu>rendered from that same server component:main:Attempted to call builder() from the server, build fails at Collecting page dataout/index.htmlpopulated — 19 398 chars each,--md-sys-color-primaryand the.darkhalf present in both. (The#mcu-stylesone being non-empty is fix: render<style>so the colors are in the server HTML #155; the#ssr-mcuone is this PR.)pnpm run lgtmclean —attwstill 🟢 on all four resolution modes, 40 tests pass.README
A note under Programmatic API saying
builderis server-safe, since that's the whole point.🤖 Generated with Claude Code