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 and StencilJS.

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 Props (docs)
  • CSS States (docs)

Usage

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

  1. 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

generate-types.ts
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 } from "path/to/jsx-types";
// The module name is typically something like 'react', 'preact'
// or whatever the package name is that provides JSX support.
declare module 'react' {
namespace JSX {
interface IntrinsicElements extends CustomElements {}
}
}

Configuration

You can configure the JSX Types Generator using the JsxTypesOptions interface. This allows you to customize how the types are generated, where they are output, and how they interact with your components.

type JsxTypesOptions = {
/** Used to get a specific path for a given component */
componentTypePath?: (name: string, tag?: string, modulePath?: string) => string;
/** Name of the file generated */
fileName?: string;
/** Path to output directory */
outdir?: string;
/** Component names to exclude form process */
exclude?: string[];
/** Used to get global type reference for components */
globalTypePath?: string;
/** Indicates if the component classes are a default export rather than a named export */
defaultExport?: boolean;
/** Include standard global events (ie - `onClick`, `onHover`, etc. */
includeDefaultDOMEvents?: boolean;
/** Used to add global element props to all component types */
globalEvents?: string;
/** Adds types to allow users to add undefined attributes or props to the custom elements */
allowUnknownProps?: boolean;
/** Adds a prefix to tag references */
prefix?: string;
/** Adds a suffix to tag references */
suffix?: string;
/** Available options for configuring the way the components description is rendered */
componentDescriptionOptions?: ComponentDescriptionOptions;
/** Uses your custom event type instead of `CustomEvent<T>` */
overrideCustomEventType?: boolean;
/** Skips the code from running */
skip?: boolean;
/** Shows contextual logs */
debug?: boolean;
};

Output

You can configure the destination and the file name of the generated type file using the outdir and fileName configuration.

{
/** Path to output directory */
outdir: 'dist',
/** File name for the types */
fileName: 'jsx-integration.d.ts'
}

Descriptions

Configurations to the component descriptions can be made using the componentDescriptionOptions property. These leverage the details from the Custom Elements Manifest to generate the descriptions for each component and utilize our CEM Description helper.

Details on how to configure this description can be found here.

{
componentDescriptionOptions: {
/** Adds a prefix to the description */
prefix: "This is a custom element: ",
/** Adds a suffix to the description */
suffix: " - Learn more at https://example.com/docs",
/** Whether to include the component's version in the description */
includeVersion: true,
}
}

Default Exports

If you component class does not provide a named export and is the default export, be sure to set defaultExport to true. This will endure the import for the class gets resolved correctly.

Types

If your components were built using TypeScript, you should define a path to your type declarations to pass that type-safety on to the JSX project.

If your types are rolled up into a single type declaration file, you can set the globalTypePath option to the location of that file.

{
globalTypePath: ".dist/types.d.ts";
}

If each of the component type definitions are split out by each component, you can use the componentTypePath to reference individual component paths.

{
componentTypePath: (name, tag) => `./types/${tag}/${name}.d.ts`;
}

Custom Event Type Handling with overrideCustomEventType

By default, the JSX Types Generator uses the standard CustomEvent<T> type for all custom events emitted by your components. However, some libraries may create custom event types.

The overrideCustomEventType option allows you to use your own event type convention instead

When enabled:

  • The generator will not explicitly use CustomEvent<T> in the generated types
  • Instead, it will use the event type exactly as specified in your components
  • This works well with frameworks that have their own event wrapper types

Use Cases

This option is particularly useful when:

  1. Your framework has its own event type system (like some versions of Stencil)
  2. You’ve created a custom event type that extends or modifies the standard CustomEvent
  3. You’re working with a TypeScript setup that has special handling for events

Example

Without this option, events might be typed like:

"onmy-change"?: (e: CustomEvent<T>) => void;

With overrideCustomEventType: true, if your event type is defined as MyCustomEvent<T>, the generator will preserve that type:

"onmy-change"?: (e: MyCustomEvent<T>) => void;

Type Flexibility with allowUnknownProps

By default, the generated types are strict, only allowing properties and events that are explicitly defined in the custom elements manifest. This ensures type safety but can sometimes be restrictive when you need to pass additional properties that aren’t formally defined in the component.

When enabled:

  • Components will accept any additional properties beyond those explicitly defined
  • This can be helpful in the following scenarios:
    • When components have undocumented properties
    • When you need to pass data attributes (data-*)
    • When using third-party libraries that attach special properties
    • During development when component APIs are still evolving

Example usage:

// With allowUnknownProps: true
<my-component
standardProp="value"
data-analytics="click-tracking" // Works with allowUnknownProps: true
unknown-attribute="something" // Works with allowUnknownProps: true
/>

Scoped Elements

There are two ways to work with scoped custom elements (elements with prefixes or suffixes):

Configuration Options: prefix and suffix

When generating your types, you can automatically add prefixes or suffixes to all tags:

// In your configuration
{
// Adds "my-" to the beginning of all component tags
prefix: "my-",
// Adds "-component" to the end of all component tags
suffix: "-component"
}

This will generate types where components like <button> become available as <my-button-component>.

Using ScopedElements Utility Type

If you’ve already generated types without prefixes/suffixes, you can use the ScopedElements utility type to create scoped versions:

scoped-types.d.ts
import type { CustomElements, ScopedElements } from "path/to/jsx-types";
declare module "my-app" {
namespace JSX {
interface IntrinsicElements extends ScopedElements<"prefix-", "-suffix"> {}
}
}

Adding DOM Event Support

DOM Event Handlers

Custom elements can respond to standard DOM events like clicks, keyboard input, and focus changes. There are two ways to add these event types to your components:

Using includeDefaultDOMEvents

The simplest approach - automatically include all standard DOM event handlers:

{
includeDefaultDOMEvents: true; // Adds onClick, onKeyDown, onFocus, etc. to all components
}

This adds common event handlers like onClick, onMouseOver, onKeyDown, etc. to all your components.

Using globalEvents for Custom Control

For more control over which events are included:

{
globalEvents: `
/** Custom event fired for all elements when special action occurs */
onMySpecialEvent?: (event: CustomEvent<{ id: string }>) => void;
`;
}

Combining Both Approaches

You can use both options together:

{
// Include standard DOM events
includeDefaultDOMEvents: true,
// Add your own custom events
globalEvents: `
/** Custom event fired for all elements when special action occurs */
onMySpecialEvent?: (event: CustomEvent<{ id: string }>) => void;
`
}

Available Default DOM Events

When using includeDefaultDOMEvents: true, the following event categories are included:

  • Mouse Events: onClick, onContextMenu, onDoubleClick, onDrag, onDragEnd, etc.
  • Keyboard Events: onKeyDown, onKeyUp, onKeyPress
  • Focus Events: onFocus, onBlur
  • Form Events: onChange, onInput, onSubmit, onReset
  • UI Events: onScroll
  • Wheel Events: onWheel
  • Animation Events: onAnimationStart, onAnimationEnd, onAnimationIteration
  • Transition Events: onTransitionEnd
  • Media Events: onLoad, onError
  • Clipboard Events: onCopy, onCut, onPaste