| name | check-rsc-compat |
| description | This skill should be used when the user asks to "check RSC compatibility", "verify server component support", "is this RSC safe", "will this work in server components", "audit for client-side code", or wants to verify components work with React Server Components. |
Check RSC Compatibility
Verify that Seams components are compatible with React Server Components by checking for runtime CSS generation, client-side hooks, and other incompatible patterns.
RSC Compatibility Requirements
For a component to be RSC-compatible:
- No runtime CSS generation - All CSS must be extracted at build time
- No client-side state - No
useState, useEffect, etc. without 'use client'
- No browser APIs - No
window, document, localStorage access
- Serializable props - No functions passed as props (unless marked client)
Quick Compatibility Check
Check a Single File
Run this script to audit a file:
import { analyzeSource } from "@artmsilva/seams-plugin-common";
import { readFileSync } from "fs";
const filename = process.argv[2];
const source = readFileSync(filename, "utf-8");
const issues: string[] = [];
const analysis = analyzeSource(source, filename);
if (analysis.hasStitchesImport) {
console.log("โ Has Seams import");
for (const usage of analysis.usages) {
if (usage.hasDynamicValues) {
console.log(`โ Dynamic values in ${usage.type} "${usage.name}" - will use CSS variables`);
} else {
console.log(`โ Static ${usage.type} "${usage.name}"`);
}
}
}
const clientPatterns = [
{ pattern: /\buseState\b/, name: "useState hook" },
{ pattern: /\buseEffect\b/, name: "useEffect hook" },
{ pattern: /\buseLayoutEffect\b/, name: "useLayoutEffect hook" },
{ pattern: /\buseRef\b/, name: "useRef hook" },
{ pattern: /\bwindow\b/, name: "window object" },
{ pattern: /\bdocument\b/, name: "document object" },
{ pattern: /\blocalStorage\b/, name: "localStorage" },
{ pattern: /\bsessionStorage\b/, name: "sessionStorage" },
{ pattern: /\baddEventListener\b/, name: "addEventListener" },
];
const hasUseClient = source.includes("'use client'") || source.includes('"use client"');
for (const { pattern, name } of clientPatterns) {
if (pattern.test(source)) {
if (hasUseClient) {
console.log(`โ ${name} (marked as client component)`);
} else {
issues.push(`โ ${name} without 'use client' directive`);
}
}
}
if (/getCssText\(\)/.test(source)) {
console.log("โ getCssText() used - ensure this is for SSR fallback only");
}
console.log("\n--- Summary ---");
if (issues.length === 0) {
console.log("โ File appears RSC-compatible");
} else {
console.log("Issues found:");
issues.forEach((issue) => console.log(` ${issue}`));
}
Run with:
npx tsx check-rsc.ts src/components/Button.tsx
Check Entire Directory
grep -rn "useState\|useEffect\|window\.|document\." src/components/ --include="*.tsx" | grep -v "'use client'"
RSC-Compatible Patterns
โ
Static Styled Components
const Button = styled("button", {
backgroundColor: "$primary",
padding: "$2 $4",
});
โ
Variants (Static)
const Button = styled("button", {
variants: {
size: {
sm: { padding: "$1 $2" },
lg: { padding: "$3 $6" },
},
},
});
โ
Dynamic css prop (Converted to CSS Variables)
<Box css={{ marginTop: dynamicValue }} />
โ
Theme Tokens
const Card = styled("div", {
backgroundColor: "$colors$background",
borderRadius: "$radii$lg",
boxShadow: "$shadows$md",
});
RSC-Incompatible Patterns
โ Functions in Styles
const Box = styled("div", {
color: () => getColor(),
});
const Box = styled("div", {
color: "var(--dynamic-color)",
});
โ Runtime State Without 'use client'
const Toggle = () => {
const [active, setActive] = useState(false);
return <Button onClick={() => setActive(!active)} />;
};
'use client';
const Toggle = () => {
const [active, setActive] = useState(false);
return <Button onClick={() => setActive(!active)} />;
};
โ Browser APIs Without 'use client'
const WindowSize = () => {
return <div>{window.innerWidth}</div>;
};
'use client';
const WindowSize = () => {
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
return <div>{width}</div>;
};
Build-Time Verification
Verify CSS Extraction
After building, check that CSS is extracted:
ls -la dist/*.css .next/static/css/*.css 2>/dev/null
grep -c "backgroundColor" dist/stitches.css
grep -c "backgroundColor.*:" dist/*.js
Verify No Runtime CSS
Check that styled components don't include CSS generation code:
grep -l "insertRule\|createElement.*style" dist/*.js
Component Architecture for RSC
Recommended Pattern
Split components into server and client parts:
components/
โโโ Button/
โ โโโ Button.tsx # Server component (styled)
โ โโโ Button.client.tsx # Client component (interactions)
โ โโโ index.ts # Re-export
Button.tsx (Server):
import { styled } from "@artmsilva/seams-react";
export const ButtonBase = styled("button", {
backgroundColor: "$primary",
variants: {
size: {
sm: { padding: "$1 $2" },
lg: { padding: "$3 $6" },
},
},
});
Button.client.tsx (Client):
'use client';
import { ButtonBase } from './Button';
export const InteractiveButton = ({ onClick, ...props }) => {
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
await onClick?.();
setLoading(false);
};
return <ButtonBase {...props} onClick={handleClick} disabled={loading} />;
};
Troubleshooting
"Cannot use useState in Server Component"
Add 'use client' directive at the top of the file.
"window is not defined"
The code is running on the server. Either:
- Add
'use client' directive
- Use
typeof window !== 'undefined' guard
- Move to
useEffect
"Styles not applying in production"
Verify the build plugin is configured and CSS is being extracted:
pnpm build
cat dist/stitches.css | head -50
Additional Resources
See references/rsc-patterns.md for more RSC architecture patterns.