
JSX Types Generator
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.
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:
- calling a function in your build pipeline
- as a plugin for the Custom Element Manifest Analyzer
Install
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:
- Install and set up the Custom Elements Manifest Analyzer
- Create a config file
Import
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
:
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:
- Your framework has its own event type system (like some versions of Stencil)
- You’ve created a custom event type that extends or modifies the standard CustomEvent
- 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:
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