Documentation

Language Server

Development Server

MCP Server

Commands

Buildless Development

Write TypeScript and import CSS directly in your browser - no build step required. cem serve lets you develop without running tsc, CSS watchers, or bundlers.

Write TypeScript Directly

Import TypeScript files directly in your demos without compilation:

<!-- In your demo -->
<script type="module" src="../src/my-element.ts"></script>

No need to:

  • Run tsc --watch
  • Set up a build pipeline
  • Wait for compilation before testing
  • Manage separate source and output directories

The dev server handles TypeScript on-demand with full source map support for debugging.

Browser Compatibility

Control which browsers your code supports using the --target flag:

cem serve --target es2020

The default target is es2022. See esbuild’s target documentation for all available targets.

Your tsconfig.json Works

The dev server respects your existing TypeScript configuration:

  • compilerOptions.target
  • compilerOptions.module
  • include and exclude patterns
  • Path mappings (via import maps)
Command-line --target flag overrides tsconfig.json settings.

Import CSS as Modules

Import CSS files directly in your components using modern CSS module syntax:

import styles from './my-element.css';

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.adoptedStyleSheets = [styles];
  }
}

No need to:

  • Run CSS preprocessors or watchers
  • Bundle stylesheets separately
  • Use inline <style> tags
  • Write CSS-in-JS template literals

Your imported CSS becomes a Constructable Stylesheet automatically, giving you better performance than inline styles.

Import Attributes (Modern Syntax)

Use the standard import attributes syntax with the with keyword:

import styles from './my-element.css' with { type: 'css' };

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.adoptedStyleSheets = [styles];
  }
}

This is the modern, standards-based syntax for importing CSS as modules. The dev server automatically handles this syntax during TypeScript transformation - import attributes are preserved and CSS files are transformed to JavaScript modules that export CSSStyleSheet objects.

Import attributes with with { type: 'css' } bypass include/exclude patterns. When you use this syntax, the dev server knows you want that CSS file transformed to a module, regardless of glob patterns.

How it works:

  1. You write: import styles from './foo.css' with { type: 'css' }
  2. The dev server rewrites it to: import styles from './foo.css?__cem-import-attrs[type]=css'
  3. When the browser requests the CSS file with those query parameters, the server transforms it to a JavaScript module
  4. Your code receives a ready-to-use CSSStyleSheet object

This approach works around esbuild’s current limitation with CSS import attributes while preserving the standard syntax in your source code.

Opt-In for Safety

CSS module transformation is opt-in to protect traditional <link> stylesheets. Specify which CSS files to transform:

Via command line:

# Transform component CSS only
cem serve --css-transform 'src/**/*.css' --css-transform 'elements/**/*.css'

Via config file:

serve:
  transforms:
    css:
      include:
        - 'src/**/*.css'
        - 'elements/**/*.css'
      exclude:
        - 'demo/**/*.css'

Via import attributes:

// This CSS file will be transformed regardless of include/exclude patterns
import styles from './my-styles.css' with { type: 'css' };
Without include patterns or import attributes, no CSS files are transformed. This prevents <link rel="stylesheet"> tags from breaking unexpectedly.

Example: CSS Module Import

Your CSS file:

/* my-element.css */
:host {
  display: block;
  color: var(--text-color, #333);
}

Import it:

import styles from './my-element.css';

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.adoptedStyleSheets = [styles];
  }
}

What you get: A CSSStyleSheet object ready to use with adoptedStyleSheets - no build step, no runtime CSS parsing.

What Works Without Building

You can use directly:

  • TypeScript files (.ts, .tsx)
  • CSS files as modules (with opt-in patterns)
  • ES modules from npm via import maps
  • Modern JavaScript syntax

You still write as-is:

  • JavaScript files (.js, .mjs)
  • HTML files
  • JSON files

Debugging

Source maps work automatically:

  • Stack traces point to your original TypeScript
  • Browser DevTools show your source files
  • Breakpoints work in TypeScript, not generated JavaScript

Configuration

Via Command Line

cem serve --target es2020 --css-transform 'src/**/*.css'

Via Config File

serve:
  target: es2020
  transforms:
    css:
      include:
        - 'src/**/*.css'

See Configuration for all options.

What’s Next?