Create a component with Pure TypeScript
In this tutorial, you'll use the official component template to generate a package-based custom component, understand how each piece works, and modify the component to make it your own.
Prerequisites
- The following packages must be installed in your Python environment:
- Node.js 24 or later must be installed. This includes npm, the package manager for JavaScript.
- Familiarity with inline custom components is recommended.
Summary
The template generates a working "Hello, World!" component with a click counter. You'll walk through the generated code, then extend it to add a reset button with a trigger value.
Here's a look at what you'll build:
Generate the project
-
Navigate to the directory where you want to create your project and run the cookiecutter generator. The generator will create a new subdirectory for your project.
Terminal -
Follow the interactive prompts. When asked for the framework, select Pure Typescript:
TerminalThis creates a
my-click-counter/directory with the following structure:
Run the template
You need two terminals running in parallel for development. The following steps use uv run to run commands inside the project's virtual environment. If a .venv doesn't exist yet, uv run creates one automatically.
-
In the first terminal, navigate to the frontend directory, install dependencies, and start the dev build watcher:
Terminal -
In a second terminal, navigate to the project root and run the example app:
Terminal -
View your running app.
You should see a "Hello, World!" heading with a "Click Me!" button. Clicking the button increments a counter that's sent back to Python. An
st.text_inputlets you specify a name which is passed to a second instance of the component.
Understand the generated code
Now that the component is running, walk through each file to understand how it works.
-
Open
my_click_counter/__init__.py:PythonThis file does two things:
-
Registers the component with
st.components.v2.component(). The first argument is a qualified name ("<package-name>.<component-name>") where<package-name>matches thenamefield in the project-levelpyproject.tomland<component-name>matches thenamefield in the component-levelpyproject.toml. The other two arguments point to the frontend assets:jsis a glob pattern that matches the JavaScript bundle produced by Vite.htmlprovides the initial markup that's rendered before the JavaScript loads. -
Defines a wrapper function (
my_click_counter) that provides a clean API. The wrapper calls the raw component withdata,default, and callback parameters. This pattern is optional but recommended. For more about these parameters, see Component mounting.
-
-
Open
my_click_counter/frontend/src/index.ts:TypeScriptThis follows the same pattern as inline components, but with TypeScript types. Here are the key pieces:
- Type definitions:
FrontendStateandComponentDatadefine the shape of the component's state and the data it receives from Python. These are used as generic parameters onFrontendRendererfor type safety. - Instance tracking: The
WeakMaptracks per-instance state (the click count) across re-renders. Since Streamlit calls your function on every re-render, you need a way to persist state between calls without re-adding event listeners. setStateValue: Sends the updated click count back to Python. This triggers a rerun, just like in inline components.- Cleanup function: The returned function removes event listeners when the component is unmounted.
- Type definitions:
The vite.config.ts builds your TypeScript into an ES module with a hashed filename (like index-a1b2c3d4.js). The pyproject.toml files tell setuptools to include these build artifacts in the Python package, and tell Streamlit where to find and serve them. For a detailed explanation of each configuration file, see Package-based components.
Modify the component
You can extend the template to add a reset button and a trigger value that fires when the counter is reset.
-
In
my_click_counter/__init__.py, make the following changes to thehtmlparameter to add a reset button and a count display:Pythonstar Tip
The copy button on the diff code blocks only copy the lines in the final result, not the deleted lines.
-
In
my_click_counter/frontend/src/index.ts, make the following changes to handle both buttons:TypeScriptThe key changes are:
- Added
was_resetto theFrontendStatetype. - Added
setTriggerValueto the destructured args. UnlikesetStateValue, trigger values are transient and reset toNoneafter each rerun. - Renamed the button to
incrementBtnand the click handler tohandleIncrement. - Named the new button
resetBtn. - Added a reset handler,
handleReset, that sets the count back to zero and fires a"was_reset"trigger. - Added a count display that updates on each click.
- Added
-
In
my_click_counter/__init__.py, make the following changes to the wrapper function to handle the new trigger:PythonThe wrapper now accepts an
on_resetcallback that defaults tolambda: None. Inside,on_num_clicks_changeuses an inline lambda since nothing needs to happen when the count changes.on_was_reset_changepasses through the caller'son_resetcallback so the app can react when the counter is reset. -
If
npm run devis still running, the frontend rebuilds automatically. Refresh your Streamlit app to see the changes. -
To try the new functionality in a clean example, replace the contents of
example.pywith the following code:Python -
Save your file and view your running app.
Build for production
When you're ready to share your component, create a production build.
-
Stop the
npm run devwatcher and thestreamlit runprocess by pressingCtrl+Cin each terminal. -
In either terminal, navigate to the frontend directory and build the frontend:
Terminal -
Navigate to the project root and build the Python wheel:
TerminalThis creates a
.whlfile in thedist/directory that you can distribute or upload to PyPI. For publishing instructions, see Publish a Component.
What's next?
- Learn more about the project structure in Package-based components.
- Understand State vs trigger values for interactive components.
- Explore Theming and styling to use Streamlit's CSS custom properties.
- Try the React + TypeScript tutorial if you want to use React.
Still have questions?
Our forums are full of helpful information and Streamlit experts.
