- Contents
- Get started with the template
- Prerequisites
- Generate a project
- Run in development mode
- Understanding the project structure
- Top-level pyproject.toml
- Component-level pyproject.toml
- Frontend configuration
- The Python API
- Glob pattern support
- Why use glob patterns?
- Glob resolution rules
- Example usage
- Development workflow
- Development mode
- Build for production
- Build a Python wheel
- Publishing your package
- Best practices
- Error handling
- Performance
- Documentation
- What's next?
Package-based components
While inline components are perfect for rapid prototyping, package-based components provide the full power of modern frontend development. This approach is ideal for complex components that require TypeScript, external dependencies, build optimization, or distribution as Python packages.
Choose package-based components when you need one of the following features:
- TypeScript support: Type safety and better developer experience
- External dependencies: React, D3, Chart.js, or other npm packages
- Build optimization: Code splitting, minification, and bundling
- Team development: Proper tooling, testing, and collaboration workflows
- Distribution: Publishing components as Python packages on PyPI
- Complex logic: Multi-file projects with organized code structure
Get started with the template
The fastest way to create a package-based component is with the official component template. It generates a complete project with all the configuration, build tooling, and boilerplate you need.
Tip
For step-by-step walkthroughs of using the template, see the tutorials:
Prerequisites
Generate a project
-
Navigate to the directory where you want to create your project, then run the following command:
TerminalThe generator will create a new subdirectory for your project.
-
Answer the generator's questions (project name, author, license) and choose between two frontend frameworks:
- React + TypeScript is best for components with complex, state-driven UIs.
- Pure TypeScript is best for lightweight components without framework overhead.
Run in development mode
To run your component in development mode, you need to install dependencies and start two simultaneous processes from separate terminals.
-
Navigate to the project root directory (e.g.
my-component/) and install the Python package in editable mode:TERMINAL A -
Navigate to the frontend directory and start the dev build watcher:
TERMINAL A -
In a second terminal, navigate to the project root and run your Streamlit app:
TERMINAL B
The npm run dev command watches for changes to your frontend code and rebuilds automatically. When you make changes, refresh your Streamlit app to see them.
Understanding the project structure
Whether you use the template or create a project manually, a package-based component follows this structure:
The rest of this page explains each piece of this structure. If you generated your project from the template, this will help you understand what the template created and how to customize it.
Top-level pyproject.toml
The root pyproject.toml configures your Python package for distribution. For more information, see the Python Packaging User Guide.
The key sections are:
[project]: Package metadata. Thenamefield is what users pass topip install.[tool.setuptools.packages.find]: Tells setuptools to automatically find your component package by matching themy_component*pattern.[tool.setuptools.package-data]: Ensures the built frontend assets (frontend/build/**/*) and the innerpyproject.tomlare included when the package is built into a wheel.
Component-level pyproject.toml
Inside your component module, a second pyproject.toml registers your component with Streamlit and specifies where the frontend assets live:
When you start a Streamlit app, Streamlit scans all installed packages for component metadata like this. For each component it finds, Streamlit serves the contents of the asset_dir directory. This makes it possible to refer to JavaScript bundles, images, and other assets within your component.
The project.name should match the name of your package as installed.
Important
The asset_dir path is relative to the component-level pyproject.toml file. All files and subdirectories within the asset directory will be served publicly by Streamlit and won't be protected by any logical restrictions in your app. Don't include sensitive information in your component's asset directory.
Frontend configuration
The frontend/ directory contains all frontend source code and configuration:
package.jsondefines your frontend dependencies and build scripts.tsconfig.jsonconfigures the TypeScript compiler.vite.config.tsconfigures Vite to build your component as an ES module library with hashed filenames.src/index.tsis the main TypeScript entry point for your component.
For clarity, the following subsections highlight the most important settings and patterns for Streamlit.
package.json includes the two most important scripts you will use during development and production:
npm run devwatches for changes and rebuilds during development.npm run buildcreates an optimized production build with hashed filenames.
The @streamlit/component-v2-lib package provides the TypeScript type definitions for the component API. Your src/index.ts entry point imports types like FrontendRenderer and FrontendRendererArgs from this package:
Tip
Use FrontendRenderer and FrontendRendererArgs directly, and extend them with your own generic type parameters for FrontendState and data shapes. This keeps your component's type signatures consistent with the Streamlit runtime and ensures you benefit from any upstream type improvements.
The following Vite settings are particularly significant for Streamlit:
base: "./"tells Vite to use relative paths so assets resolve correctly when served by Streamlit.outDir: "build"outputs built files tofrontend/build/. This must match theasset_dirin your component-levelpyproject.toml.formats: ["es"]configures Vite to output an ES module, which is the format Streamlit expects when loading your component.fileName: "index-[hash]"tells Vite to include a content hash in the output filename (e.g.index-a1b2c3d4.js) for cache busting. You reference these with glob patterns in the Python API.
The Python API
The __init__.py file registers your component and optionally provides a wrapper function:
A few things to note:
- The first argument to
st.components.v2.component()is a qualified name:"<package-name>.<component-name>". This matches theproject.namein your top-levelpyproject.tomland thenamein your component-levelpyproject.toml. - The
jsparameter uses a glob pattern ("index-*.js") to match the hashed build output. - The wrapper function (
my_component) provides a clean API for users of your component. It's optional but recommended.
For more about registration and mounting, see Component registration and Component mounting.
Glob pattern support
A glob pattern is a string with wildcards that matches filenames. For example, index-*.js matches any file whose name starts with index- and ends with .js. Package-based components use glob patterns to reference build outputs with hashed filenames.
Why use glob patterns?
Modern build tools like Vite include a content hash in output filenames. The hash changes whenever the file content changes, which ensures browsers load the latest version instead of serving a stale cached copy:
Glob resolution rules
- Pattern matching:
index-*.jsmatchesindex-a1b2c3d4.js - Single file requirement: A pattern must resolve to exactly one file.
- Security: Matched files must be within the
asset_dir. - Relative paths: Patterns are resolved relative to
asset_dir.
Example usage
Note
If a glob pattern matches zero files or multiple files, Streamlit raises a clear error message to help you debug the issue.
Development workflow
Development mode
During development, the npm run dev command watches your source files and rebuilds on changes. You need two terminals:
After the frontend rebuilds, refresh your Streamlit app to see the changes.
Build for production
When you're ready to distribute your component, create an optimized production build:
This generates hashed files in the build/ directory that your glob patterns will match.
Build a Python wheel
Package your component for distribution:
This creates a wheel in the dist/ directory that includes your compiled frontend assets.
Publishing your package
For detailed instructions on publishing your component to PyPI, see Publish a Component.
The basic steps are:
- Build the frontend:
npm run build(from thefrontend/directory) - Build the wheel:
uv build(from the project root) - Upload to PyPI:
uv publishorpython -m twine upload dist/*
After publishing, users can install your component with:
Best practices
Error handling
Implement error handling in your frontend code to provide a good experience when something goes wrong:
Performance
- Use code splitting for large dependencies.
- Implement lazy loading for heavy components.
- Optimize bundle sizes with tree shaking (Vite does this by default in production builds).
Documentation
Provide comprehensive documentation for your component:
- Python docstrings with parameter descriptions
- TypeScript interfaces for data shapes
- Usage examples
- A
README.mdin your project root
What's next?
- Follow the tutorials to build your first package-based component:
- Learn about State vs triggers for interactive functionality.
- Explore Theming and styling for beautiful components.
- Check out Publishing components for distribution strategies.
Still have questions?
Our forums are full of helpful information and Streamlit experts.
