Skip to content
A workbench with tools, html, css, javascript, and JSX logo

JSX Types Generator

Generate JSX types for custom elements / web components

This package is designed to generate JSX types for your custom elements. These types will generate inline documentation, autocomplete, and type-safe validation for your custom elements in frameworks that use JSX like React (19+), Preact, StencilJS, and SolidJS.

demo of autocomplete features for custom elements in a jsx project

This allows developers to use your custom elements in their JSX projects with full type support, making it easier to integrate and use your components.

Types will be generated for all custom elements defined in your Custom Elements Manifest.

This includes types and documentation for:

  • Custom elements (types and docs)
  • Attributes (types and docs)
  • Properties (types and docs)
  • Events (types and docs)
  • Methods (types and docs)
  • Slots (docs)
  • CSS Custom Properties (docs)
  • CSS States (docs)

Usage

This package includes two ways to generate the custom data config file:

  1. programatically calling a function in your build pipeline
  2. as a plugin for the Custom Element Manifest Analyzer

Install

Terminal window
npm i -D @wc-toolkit/jsx-types

Build Pipeline

import { generateJsxTypes, JsxTypesOptions } from "@wc-toolkit/jsx-types";
import manifest from "./path/to/custom-elements.json";
const options: JsxTypesOptions = {...};
generateJsxTypes(manifest, options);

CEM Analyzer

Set-up

Ensure the following steps have been taken in your component library prior to using this plugin:

Import

custom-elements-manifest.config.js
import { jsxTypesPlugin } from "@wc-toolkit/jsx-types";
const options = {...};
export default {
plugins: [
jsxTypesPlugin(options)
],
};

Implementation

In order for teams to take advantage of this, all they need to do is import the types in their project. There are two ways to configure the JSX types:

Option 1: TSConfig Configuration

Add the types to your tsconfig.json:

{
"compilerOptions": {
"types": ["path/to/jsx-types"]
}
}

Option 2: TypeScript Declaration File

Create a declaration file and extend JSX’s IntrinsicElements:

custom-elements-types.d.ts
import type { CustomElements, CustomCssProperties } from "path/to/jsx-types";
declare module "my-library" {
namespace JSX {
interface IntrinsicElements extends CustomElements {}
}
export interface CSSProperties extends CustomCssProperties {}
}

NOTE: Libraries will have their own module names you will need to use when extending the IntrinsicElements interface. For example, Preact requires you to use the "preact" module name instead of "my-library" (declare module "preact") and StencilJS uses “@stencil/core” (declare module "@stencil/core").

Configuration Options

The JsxTypesOptions interface provides several configuration options to customize how types are generated for your project:

Basic Options

fileName

  • Type: string
  • Default: "custom-element-jsx.d.ts"
  • Description: The name of the generated type definition file.
{
fileName: "my-components.d.ts"
}

outdir

  • Type: string
  • Default: "./"
  • Description: The output directory where the generated types file will be saved.
{
outdir: "./types"
}

exclude

  • Type: string[]
  • Default: []
  • Description: Array of component names to exclude from type generation.
{
exclude: ["my-internal-component", "my-deprecated-component"]
}

Import Configuration

componentTypePath

  • Type: (name: string, tag?: string, modulePath?: string) => string
  • Description: A function that returns the import path for each component. This is useful when you need to customize the import statements in the generated types.
{
componentTypePath: (name, tagName) =>
`my-lib/components/${tagName}/${tagName}.js`
}

globalTypePath

  • Type: string
  • Description: When provided, generates a single import statement for all components from this path instead of individual imports. This is useful if your library has a barrel file that exports all components.
{
globalTypePath: "my-lib"
}

defaultExport

  • Type: boolean
  • Default: false
  • Description: Set to true if your component classes use default exports instead of named exports.
{
defaultExport: true
}

Event Configuration

stronglyTypedEvents

  • Type: boolean
  • Default: false
  • Description: This feature is highly recommended for better type safety and autocomplete. Creates event types where the event’s target is strongly typed to the custom element, providing better autocomplete and type safety for event handlers. When enabled, e.detail and e.target are strongly typed.
{
stronglyTypedEvents: true
}

includeDefaultDOMEvents

  • Type: boolean
  • Default: false
  • Description: Includes standard DOM events (e.g., onClick, onHover, etc.) in the generated types. The down side is that it can pollute the component API with attributes that aren’t relevant to the component.
{
includeDefaultDOMEvents: true
}

globalEvents

  • Type: string
  • Description: TypeScript type reference for global event props to add to all component types. This can be useful for adding custom events or event handlers to all components for things like custom telemetry.
{
globalEvents: "React.DOMAttributes<HTMLElement>"
}

Additional Options

allowUnknownProps

  • Type: boolean
  • Default: false
  • Description: Allows users to add undefined attributes or props to the custom elements without TypeScript errors.
{
allowUnknownProps: true
}

excludeCssCustomProperties

  • Type: boolean
  • Default: false
  • Description: Excludes CSS custom property types from generation.
{
excludeCssCustomProperties: true
}

tagFormatter

  • Type: (tagName: string) => string
  • Description: Optional function to format tag names before processing. Useful for adding prefixes, suffixes, or transforming tag names.
{
tagFormatter: (tagName) => tagName.replace("my-", "custom-")
}

Utility Options

skip

  • Type: boolean
  • Default: false
  • Description: Skips the entire type generation process when set to true.
{
skip: process.env.SKIP_TYPES === "true"
}

debug

  • Type: boolean
  • Default: false
  • Description: Enables debug logging to help troubleshoot type generation issues.
{
debug: true
}

Deprecated Options

prefix (deprecated)

  • Type: string
  • Description: Use tagFormatter instead. Adds a prefix to tag references.

suffix (deprecated)

  • Type: string
  • Description: Use tagFormatter instead. Adds a suffix to tag references.

overrideCustomEventType (deprecated)

  • Type: boolean
  • Default: false
  • Description: This feature never worked as intended and will be removed in the next major version.

Framework-Specific Considerations

SolidJS

When using these generated types with SolidJS, there are several important considerations to ensure proper integration:

TypeScript Declaration

If you are using a custom TypeScript declaration file, SolidJS has a custom type to include the attribute prefixes, so you will need to use CustomElementsSolidJs instead of CustomElements. For example:

custom-elements-types.d.ts
import type { CustomElementsSolidJs, CustomCssProperties } from "path/to/jsx-types";
declare module "solid-js" {
namespace JSX {
interface IntrinsicElements extends CustomElementsSolidJs {}
}
export interface CSSProperties extends CustomCssProperties {}
}

Property Binding

SolidJS generates special type definitions that include property prefixes to handle different binding scenarios:

  • attr:propertyName - For attribute binding (string values)
  • prop:propertyName - For property binding (any type)
  • bool:propertyName - For boolean properties

This allows SolidJS to properly handle web component properties:

// Attribute binding (as string)
<my-component attr:value={someString} />
// Property binding (with signals or objects)
<my-component prop:value={someSignal()} />
// Boolean property
<my-component bool:disabled={isDisabled} />

Custom Events

SolidJS uses the on: prefix for custom events. The generated types include proper event handler types:

import { createSignal } from 'solid-js';
const [value, setValue] = createSignal('');
<my-input
prop:value={value()}
on:my-input={(e) => {
// e.target is strongly typed when stronglyTypedEvents is enabled
setValue(e.target.value);
}}
on:my-change={(e) => {
// e.detail is strongly typed when stronglyTypedEvents is enabled
console.log('Changed:', e.detail);
}}
/>

innerHTML and textContent

SolidJS types also include innerHTML and textContent properties for setting element content:

<my-component innerHTML="<strong>Bold text</strong>" />
<my-component textContent="Plain text content" />

Reactive Property Updates

When working with reactive values in SolidJS, always use the prop: prefix for non-string properties:

import { createSignal } from 'solid-js';
const [isOpen, setIsOpen] = createSignal(false);
const [items, setItems] = createSignal([]);
<my-dialog prop:open={isOpen()} />
<my-list prop:items={items()} />

Refs and Methods

Access custom element methods using SolidJS refs:

import { onMount } from 'solid-js';
let dialogRef: any;
onMount(() => {
// Call custom element methods
dialogRef?.show();
});
<my-dialog ref={dialogRef}>
Dialog content
</my-dialog>

React

For React 19+ projects, the types work with the native custom element support:

<my-component
value="hello"
disabled={false}
onmy-event={(e) => console.log(e)}
/>

Preact

Preact requires extending the "preact" module:

declare module "preact" {
namespace JSX {
interface IntrinsicElements extends CustomElements {}
}
}

StencilJS

StencilJS requires extending the "@stencil/core" module:

declare module "@stencil/core" {
namespace JSX {
interface IntrinsicElements extends CustomElements {}
}
}

Complete Configuration Example

Here’s a comprehensive example showing all commonly used options:

import { generateJsxTypes } from "@wc-toolkit/jsx-types";
import manifest from "./custom-elements.json";
generateJsxTypes(manifest, {
// Output configuration
fileName: "custom-element-jsx.d.ts",
outdir: "./types",
// Component filtering
exclude: ["internal-component"],
// Import configuration
componentTypePath: (name, tagName, modulePath) => {
return `my-library/components/${tagName}/${tagName}.js`;
},
defaultExport: true,
// Event configuration
stronglyTypedEvents: true,
includeDefaultDOMEvents: true,
// Tag formatting
tagFormatter: (tagName) => tagName.toLowerCase(),
// Additional features
allowUnknownProps: false,
excludeCssCustomProperties: false,
// Development
debug: process.env.DEBUG === "true",
skip: false,
});