Skip to main content
Version: v5 (Preview)

Framework Types

React 19, Vue 3, Solid, Svelte, and Preact can all render custom elements directly, so consumers of your library don't need wrapper components to use <my-button> in a template. What they don't get out of the box is type-checking: the framework's TypeScript types don't know your tags or their props.

@stencil/types-output-target generates a type definition file for each framework that adds your components to its known elements. It covers each component's props and events, and produces types only, with no runtime code. For Angular, React 18, or framework features like v-model and server-side rendering, use the Angular, React, or Vue wrappers instead.

Setup​

Run the wizard in your Stencil project and pick the frameworks to generate types for:

stencil add @stencil/types-output-target

Or set it up by hand. Install the package:

npm install @stencil/types-output-target --save-dev

Then add the output target, with a path for each framework you want:

stencil.config.ts
import { Config } from '@stencil/core';
import { typesOutputTarget } from '@stencil/types-output-target';

export const config: Config = {
outputTargets: [
typesOutputTarget({
reactTypesPath: 'dist/types',
vueTypesPath: 'dist/types',
}),
],
};

stencil build now writes one file per framework:

dist/types
├── react-native-types.d.ts
└── vue-native-types.d.ts

Publishing the Types​

Add an entry for each file to your package.json exports, so consumers can import it. List the framework as an optional peer dependency, so TypeScript can resolve the framework's own types from your package:

package.json
{
"exports": {
"./react-native-types": {
"types": "./dist/types/react-native-types.d.ts"
},
"./vue-native-types": {
"types": "./dist/types/vue-native-types.d.ts"
}
},
"peerDependencies": {
"@types/react": ">=19",
"vue": ">=3"
},
"peerDependenciesMeta": {
"@types/react": { "optional": true },
"vue": { "optional": true }
}
}

Consumer Usage​

A consuming app imports the types once, anywhere in its source:

import 'my-library/react-native-types';

Your tags are then type-checked like any built-in element:

<my-button variant="secondary">Save</my-button>

The consumer still needs to load and register the components themselves - see Publishing.

API​

reactTypesPath​

Where to write the React types. A directory, in which case the file is named react-native-types.d.ts, or a full path ending in .d.ts. Requires React 19 or later in the consuming app.

vueTypesPath​

Where to write the Vue 3 types. Default file name: vue-native-types.d.ts.

solidTypesPath​

Where to write the Solid types. Default file name: solid-native-types.d.ts.

svelteTypesPath​

Where to write the Svelte types. Default file name: svelte-native-types.d.ts.

preactTypesPath​

Where to write the Preact types. Default file name: preact-native-types.d.ts.

excludeComponents​

Tag names to leave out of the generated types.

typesOutputTarget({
reactTypesPath: 'dist/types',
excludeComponents: ['my-internal-component'],
}),