Development Workflow
cem generate when APIs change, use cem serve for live development. Skip regenerating for styling/implementation tweaks.
cem uses a manifest-driven development approach where you write custom
elements with JSDoc documentation, generate a JSON manifest from your source
code, and use that manifest to power developer tooling. The manifest enables
LSP features
like autocomplete and validation in your editor,
provides AI assistants with component information through
MCP
, and drives
the dev server’s automatic documentation and
interactive
controls
. This means your documentation isn’t just
comments—it becomes the foundation for your entire development experience.
The workflow follows a continuous cycle: write components with JSDoc, generate the manifest to capture their APIs, serve them with hot reload for rapid iteration, test with interactive knobs and manual validation, then edit based on feedback. For quick styling or implementation tweaks you can skip regenerating the manifest, but when you change public APIs (properties, slots, events, CSS parts) you’ll regenerate to keep tooling in sync. The dev server provides buildless TypeScript transformation , automatic import maps from package.json, and live reload, making the edit-test loop nearly instantaneous.
The Core Cycle
cem follows a manifest-driven development workflow with five key phases:
1. Write
Create your custom element with JSDoc documentation:
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
/**
* Displays a personalized greeting message
*
* @summary A simple greeting component
*/
@customElement('hello-world')
export class HelloWorld extends LitElement {
static styles = css`
:host {
display: block;
font-family: sans-serif;
}
#greeting {
/** Text color (default: currentColor) */
color: var(--greeting-color, currentColor);
padding: 1rem;
}
`;
/**
* The name to greet
*/
@property() name = 'World';
render() {
return html`
<!-- The greeting text container -->
<div id="greeting" part="greeting">
Hello, ${this.name}!
<!-- Default slot for custom content -->
<slot></slot>
</div>
`;
}
}Use JSDoc to document all public APIs: attributes, properties, slots, events, CSS parts, and CSS custom properties. See Documenting Components for complete JSDoc tag reference and examples.
2. Generate
Run cem generate to create or update your manifest:
cem generateThis analyzes your code and produces custom-elements.json with metadata about your components. Regenerate after adding new components or changing public APIs (properties, attributes, slots, events, CSS APIs), and before committing to keep the manifest in sync. For quick styling or implementation tweaks during development, you can skip regeneration since cem serve works without it.
The manifest powers LSP features like autocomplete and validation, enables MCP integration for AI assistants, drives the dev server’s component listing, and supports documentation generation.
3. Serve
Start the development server:
cem serveThe server runs at http://localhost:8000 by default and automatically opens your browser to the element listing page. It provides:
- Component listing - Browse all elements discovered from your manifest
- Live demos - See components in action with interactive controls
- Hot reload - Automatic refresh when source files change
- Buildless TypeScript - Import
.tsfiles directly without compilation - Import maps - Use npm packages without bundling
The server watches your source files and automatically reloads the browser when changes are detected. For implementation tweaks and styling changes, you’ll see updates immediately without regenerating the manifest.
Common dev server options:
# Use a different port
cem serve --port 3000
# Start in chromeless mode for testing
cem serve --rendering=chromeless
# Disable live reload
cem serve --no-reloadSee Serve Command Reference for all available options.
4. Test
Interact with your components in the browser by trying different property values, testing user interactions like clicks and typing, verifying responsive behavior, checking accessibility with screen readers, and testing across browsers. Use interactive knobs to tweak element attributes and properties in real-time.
5. Edit
Based on testing, make changes to your component. Quick fixes like styling changes, implementation details, internal logic, and performance optimizations don’t need a manifest update. API changes—adding properties, attributes, slots, events, or CSS APIs, changing types or defaults, or updating documentation—require regenerating the manifest:
cem generateThe dev server will detect the change and reload automatically.
Integration Points
LSP Integration
The Language Server Protocol uses your manifest to provide autocomplete when using elements in HTML, hover documentation for attributes, validation of slot names and attribute values, and go-to-definition from usage to source. See LSP Integration for setup.
MCP Integration
The Model Context Protocol gives AI assistants access to your components, allowing them to understand component APIs, generate correct HTML with proper slots, suggest appropriate attribute values, and validate component usage. See MCP Integration for setup.
CI/CD Integration
Automate manifest generation in your build pipeline:
# GitHub Actions example
- name: Generate manifest
run: |
npm run analyze # or: cem generate
git diff --exit-code custom-elements.jsonThis ensures the manifest stays in sync with code changes.
Best Practices
Documentation-First Development
Write JSDoc before implementing features:
- Document the API - Define properties, slots, events
- Generate manifest - See how it looks
- Implement - Write the component code
- Test - Verify behavior matches documentation
This ensures documentation drives implementation, not the other way around.
Incremental Regeneration
You don’t need to regenerate the manifest after every change:
Skip regeneration when tweaking styles, fixing implementation bugs, refactoring internal code, or adding private methods. Regenerate for public API changes, documentation updates, before committing, and before publishing.
The --watch flag can help during active development:
cem generate --watchcem generate --watch alongside the dev server, it
rebuilds the manifest itself in-memory when your sources change.
Demo-Driven Development
Create demos as you build:
- Start with demo - Write the HTML you want to work
- Implement component - Make the demo functional
- Refine demo - Add edge cases and variations
- Document patterns - Add descriptions and guidance
Demos become living documentation and test cases.
Workspace Organization
Structure your project with our recommended layout for efficient workflows:
my-components/
├── elements/
│ ├── hello-world/
│ │ ├── hello-world.ts # Component
│ │ ├── hello-world.css # Styles
│ │ └── demo/
│ │ ├── basic.html # Basic demo
│ │ └── variants.html # Variations
│ └── my-card/
│ ├── my-card.ts
│ └── demo/
│ └── index.html
├── .config/
│ └── cem.yaml # `cem` configuration
├── custom-elements.json # Generated manifest
└── package.jsonThis structure keeps components self-contained, co-locates demos with components, makes navigation easy, and works well with monorepos.
Common Workflows
Adding a New Component
# 1. Create component file
mkdir -p elements/my-button/demo/
touch elements/my-button/my-button.ts
# 2. Write component with JSDoc
# 3. Generate manifest
cem generate
# 4. Create demo
touch elements/my-button/demo/index.html
# 5. Start dev server
cem serve
# 6. Test and iterateUpdating Component APIs
# 1. Update JSDoc and code
# 2. Regenerate manifest
cem generate
# 3. Verify in dev server (refreshes automatically)
# Already running: cem serve
# 4. Update demos if needed
# 5. Test changesPublishing Components
# 1. Ensure manifest is current
cem generate
# 2. Run tests
npm test
# 3. Build if needed
npm run build
# 4. Publish
npm publishThe manifest is included in your package and enables LSP/MCP for consumers.
See Also
- Getting Started - First project walkthrough
- Documenting Components - JSDoc usage guide and examples
- Examples Overview - Starter project templates
- Working with Demos - Demo organization strategies
- Buildless Development - TypeScript without builds
- Using LSP Features - Editor integration tips
- Troubleshooting - Common issues and solutions