webflow/webflow-code-component:pre-deploy-check
Pre-deployment validation for Webflow Code Components. Checks bundle size, dependencies, prop configurations, SSR compatibility, styling setup, and common issues before running webflow library share.
npx skills add https://github.com/webflow/webflow-skills --skill webflow-code-component:pre-deploy-check
Validate code components before deployment to catch issues early.
Use when:
webflow library shareDo NOT use when:
library.name, library.components)"./src/**/*.webflow.@(js|jsx|mjs|ts|tsx)" covering all supported extensionsglobals path if specified — file must exist and be importablebundleConfig path if specified — file must exist@webflow/webflow-cli installed@webflow/data-types installed@webflow/react installed.webflow.tsx / .webflow.ts files matching the glob pattern.webflow.tsx files:declareComponent from @webflow/reactprops from @webflow/data-types (if props are defined)declareComponent is called with the component and a config objectname is provided in the configname properties and appropriate defaultValue where applicable{ href, target, preload } object)options array)options object is present, validate:applyTagSelectors is a boolean (default: false) — enables site tag selectors in Shadow DOMssr is a boolean (default: true) — controls server-side renderinguseEffect or guarded blocks:window, document, localStorage, sessionStorage, navigatorssr: false in options if component is purely interactive or browser-dependent.webflow.tsx or via globals filevar(--variable-name, fallback)font-family: inheritapplyTagSelectors: true is set in component optionsprops.Slot) AND imports/uses useContext or a Context Provider:10. Run TypeScript/build check:
11. Check bundle size:
12. Run local bundle test (optional, suggest to user):
npx webflow library bundle --public-path http://localhost:4000/ to test bundling before sharing--debug-bundler flag to inspect the final webpack config13. CSS-in-JS library detection:
@webflow/styled-components-utils is installed and styledComponentsShadowDomDecorator is exported from globals decorators array@emotion/styled, @emotion/react, @mui/material): verify @webflow/emotion-utils is installed and emotionShadowDomDecorator is exported from globals decorators array14. Tailwind CSS detection:
tailwindcss in dependencies):@tailwindcss/postcss is installedpostcss.config.mjs exists with @tailwindcss/postcss plugin@import "tailwindcss" in globals.css)15. Sass/Less preprocessor detection:
.scss files or sass in dependencies): verify sass and sass-loader are installed, and a webpack config adds the .scss rule.less files or less in dependencies): verify less and less-loader are installed, and a webpack config adds the .less rulebundleConfig is set in webflow.json pointing to the webpack config16. Webpack custom config validation (if bundleConfig is specified):
module.exports)entry, output, target (these are silently filtered out)module.rules uses function syntax (currentRules) => { ... }, not an array17. Generate validation report:
| Check | Severity | Description |
|-------|----------|-------------|
| webflow.json exists | Error | Required for CLI |
| Dependencies installed | Error | @webflow/webflow-cli, @webflow/data-types, @webflow/react |
| Component files exist | Error | React + definition files present |
| declareComponent called | Error | Required in .webflow.tsx with correct imports |
| Valid prop types | Error | Only the 11 supported types (Text/String, RichText, TextNode, Link, Image, Number, Boolean, Variant, Visibility, Slot, ID) |
| Build succeeds | Error | No compilation errors |
| Bundle size < 50MB | Error | Maximum bundle limit enforced by Webflow |
| Check | Severity | Description |
|-------|----------|-------------|
| Props have defaults | Warning | Better designer experience |
| SSR compatibility | Warning | Browser APIs, dynamic content, heavy UI, non-deterministic output |
| Styles imported | Warning | Styles may not appear in Shadow DOM |
| Site class usage | Warning | Site classes don't work in Shadow DOM — use component-specific classes |
| Shadow DOM + Context | Warning | Slots prevent React Context sharing between parent/child |
| CSS-in-JS decorator | Warning | styled-components/Emotion need Shadow DOM decorators |
| Tailwind setup | Warning | Missing postcss config or globals import |
| Sass/Less setup | Warning | Missing loaders or webpack config |
| Webpack blocked props | Warning | entry, output, target overrides are silently ignored |
| Webpack rules format | Warning | Must use function syntax, not array |
| Component options | Warning | applyTagSelectors and ssr must be booleans |
| Glob pattern coverage | Warning | Pattern should cover all supported extensions (.js, .jsx, .mjs, .ts, .tsx) |
| bundleConfig file | Warning | File must exist if specified in webflow.json |
| Check | Severity | Description |
|-------|----------|-------------|
| Component count | Info | Number of components |
| Prop count | Info | Props per component |
| Bundle size | Info | Current bundle size vs 50MB limit |
| Frameworks detected | Info | CSS-in-JS, Tailwind, Sass/Less usage |
User prompt:
Check if my components are ready to deploy
Response:
🔍 Build Validation: My Component Library
Running pre-deployment checks...
---
## Project Structure
✅ webflow.json found
✅ library.name: "My Component Library"
✅ library.components: "./src/**/*.webflow.@(js|jsx|mjs|ts|tsx)"
✅ globals: "./src/globals.ts"
✅ bundleConfig: not specified (using defaults)
---
## Dependencies
✅ @webflow/webflow-cli: installed
✅ @webflow/data-types: installed
✅ @webflow/react: installed
✅ react: installed
✅ typescript: installed
---
## Frameworks Detected
✅ Tailwind CSS detected
✅ @tailwindcss/postcss installed
✅ postcss.config.mjs found
✅ globals.css imports Tailwind
---
## Components Found (4)
### 1. Button ✅
- File: src/components/Button/Button.webflow.tsx
- Imports: ✅ declareComponent, ✅ props, ✅ Button component
- Props: label (Text), variant (Variant), disabled (Boolean)
- All props have defaults ✅
- SSR compatible ✅
- Options: applyTagSelectors: true ✅
- Styles imported ✅
### 2. Card ✅
- File: src/components/Card/Card.webflow.tsx
- Imports: ✅ declareComponent, ✅ props, ✅ Card component
- Props: title (Text), image (Image), content (RichText), link (Link)
- All props have defaults ✅
- SSR compatible ✅
- Styles imported ✅
### 3. Modal ⚠️
- File: src/components/Modal/Modal.webflow.tsx
- Imports: ✅ declareComponent, ✅ props, ✅ Modal component
- Props: title (Text), isOpen (Boolean), content (Slot)
- All props have defaults ✅
- ⚠️ SSR Issue: Uses `document.body` in render
→ Suggestion: Add `ssr: false` to options or wrap in useEffect
- Styles imported ✅
### 4. CartIcon ⚠️
- File: src/components/CartIcon/CartIcon.webflow.tsx
- Imports: ✅ declareComponent, ✅ CartIcon component
- Props: none
- ⚠️ Missing props - component has no editable properties
→ Suggestion: Consider adding props for designer customization
- ⚠️ Uses localStorage without SSR guard
→ Suggestion: Wrap in useEffect or useState initializer
- Styles imported ✅
---
## Build Test
✅ Build completed successfully
✅ No TypeScript errors
✅ No webpack errors
✅ Bundle size: 2.3MB / 50MB limit
---
## Summary
| Category | Status |
|----------|--------|
| Project Structure | ✅ Pass |
| Dependencies | ✅ Pass |
| Frameworks | ✅ Pass |
| Components | ⚠️ 2 warnings |
| Build | ✅ Pass |
---
## Ready to Deploy: ⚠️ WITH WARNINGS
You can deploy, but consider fixing these issues:
1. **Modal SSR Issue**
Add to Modal.webflow.tsx:
options: {
ssr: false
}
2. **CartIcon localStorage Usage**
Update CartIcon.tsx:
const [count, setCount] = useState(() => {
if (typeof window === "undefined") return 0;
return parseInt(localStorage.getItem("cartCount") || "0");
});
---
💡 To deploy: `npx webflow library share`
💡 To test locally first: `npx webflow library bundle --public-path http://localhost:4000/`
Example: Build Failure
🔍 Build Validation: My Component Library
Running pre-deployment checks...
---
## Project Structure
✅ webflow.json found
⚠️ library.components uses narrow glob: "./src/**/*.webflow.tsx"
→ Recommendation: Use "./src/**/*.webflow.@(js|jsx|mjs|ts|tsx)" to cover all supported extensions
---
## Dependencies
❌ Missing: @webflow/react
Fix: npm install --save-dev @webflow/react
---
## Build Test
❌ Build Failed
Error in src/components/Button/Button.webflow.tsx:
Module not found: Error: Can't resolve '@webflow/react'
---
## Summary
| Category | Status |
|----------|--------|
| Dependencies | ❌ 1 error |
| Build | ❌ Failed |
---
## Ready to Deploy: ❌ NO
Fix the following before deployment:
1. **Install missing dependency**
npm install --save-dev @webflow/react
2. **Re-run validation**
After installing, run this check again.
Example: CSS-in-JS Missing Decorator
🔍 Build Validation: My Component Library
---
## Frameworks Detected
⚠️ styled-components detected but Shadow DOM decorator not configured
→ Install: npm install @webflow/styled-components-utils
→ Add to globals.ts:
import { styledComponentsShadowDomDecorator } from "@webflow/styled-components-utils";
export const decorators = [styledComponentsShadowDomDecorator];
→ Reference globals in webflow.json:
{ "library": { "globals": "./src/globals.ts" } }
Without this, styled-components styles will be injected into document.head
instead of the Shadow DOM, and your components will appear unstyled.
Example: Webpack Config Issues
🔍 Build Validation: My Component Library
---
## Webpack Configuration
⚠️ webpack.webflow.js: `module.rules` uses array syntax
→ Must use function syntax: `rules: (currentRules) => { return [...]; }`
→ Array syntax will not work — the function receives current rules to extend
⚠️ webpack.webflow.js: overrides `output` property
→ The `output` property is blocked and will be silently ignored
→ Blocked properties: entry, output, target
💡 Use `--debug-bundler` flag to inspect the final merged webpack config:
npx webflow library bundle --debug-bundler
Example: Shadow DOM Context Warning
## Components Found (2)
### 1. ThemeProvider ⚠️
- File: src/components/ThemeProvider/ThemeProvider.webflow.tsx
- Props: theme (Variant), children (Slot)
- ⚠️ Shadow DOM + React Context Issue:
Component uses Slot prop AND React Context (ThemeContext).
Children placed in slots render in separate Shadow DOM containers
with their own React roots — they cannot access this Context.
Alternatives for cross-component state:
- Nano Stores (lightweight reactive state)
- Custom events (window.dispatchEvent/addEventListener)
- URL parameters (for shareable state)
- Browser storage (localStorage/sessionStorage)
Run checks in this order for efficiency:
Look for these patterns that indicate SSR issues:
// Direct browser API usage (will break SSR)
window.innerWidth
document.getElementById
localStorage.getItem
navigator.userAgent
sessionStorage.getItem
// Dynamic/personalized content (may cause hydration mismatch)
// User-specific dashboards, authenticated views
// Heavy/interactive UI (SSR adds no value, re-renders anyway)
// Charts, 3D scenes, maps, animation-driven elements
// Non-deterministic output (differs server vs client)
Math.random()
new Date().toLocaleString()
// Safe patterns (in useEffect or state initializer)
useEffect(() => {
// Browser APIs here are fine
}, []);
useState(() => {
if (typeof window === "undefined") return default;
return window.innerWidth;
});
When SSR issues are found, prominently suggest the ssr: false option:
export default declareComponent(MyComponent, {
name: "My Component",
options: {
ssr: false // Disables server-side rendering
},
});
Check project dependencies and files to detect styling frameworks:
styled-components:
styled-components in package.json dependencies@webflow/styled-components-utils installedstyledComponentsShadowDomDecorator in globals decorators arrayEmotion / Material UI:
@emotion/styled, @emotion/react, or @mui/material in dependencies@webflow/emotion-utils installedemotionShadowDomDecorator in globals decorators arrayTailwind CSS:
tailwindcss in dependencies@tailwindcss/postcss installedpostcss.config.mjs with @tailwindcss/postcss plugin@import "tailwindcss")Sass:
.scss files in src or sass in dependenciessass and sass-loader installed as dev dependencies.scss rule using function syntax for module.rulesbundleConfig set in webflow.jsonLess:
.less files in src or less in dependenciesless and less-loader installed as dev dependencies.less rule using function syntax for module.rulesbundleConfig set in webflow.jsonWhen bundleConfig is specified in webflow.json:
module.exports = { ... }entry, output, targetmodule.rules must be a function, not an array: rules: (currentRules) => { ... }ModuleFederationPlugin and MiniCssExtractPlugin are auto-deduplicated| Error | Cause | Fix |
|-------|-------|-----|
| "Can't resolve '@webflow/react'" | Missing dependency | npm i -D @webflow/react |
| "Cannot find module './Component'" | Wrong import path | Check relative paths |
| "Type 'X' is not assignable" | TypeScript error | Fix type mismatch |
| "Unexpected token" | Syntax error | Check JSX/TS syntax |
| "Maximum call stack" | Circular import | Break dependency cycle |
| Bundle exceeds 50MB | Too many/large dependencies | Tree-shake, lazy load, replace heavy libs |
| Styles not appearing | Missing Shadow DOM decorator | Add CSS-in-JS decorator or import styles in .webflow.tsx |
Quick wins for reducing bundle size:
Take webflow/webflow-code-component:pre-deploy-check from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npm, npx.
Without those the skill loads but fails at the first command.