├── .github ├── dependabot.yml └── workflows │ └── main.yaml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.json ├── .npmrc ├── .nvmrc ├── .prettierrc.json ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── lerna.json ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── streamlit-component-lib-react-hooks ├── .gitignore ├── .lintstagedrc.json ├── .prettierignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── eslint.config.js ├── package.json ├── src │ ├── ErrorBoundary.tsx │ ├── StreamlitProvider │ │ ├── StreamlitProvider.tsx │ │ ├── index.ts │ │ └── useRenderData.ts │ ├── index.tsx │ └── useNullableRenderData.ts ├── tsconfig.base.json ├── tsconfig.cjs.json ├── tsconfig.esm.json └── tsconfig.json └── template ├── LICENSE ├── MANIFEST.in ├── my_component ├── __init__.py └── frontend │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── src │ ├── MyComponent.tsx │ └── index.tsx │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts └── setup.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/streamlit-component-lib-react-hooks" 5 | schedule: 6 | interval: daily 7 | 8 | - package-ecosystem: github-actions 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/workflows/main.yaml: -------------------------------------------------------------------------------- 1 | name: Build, Test, and Publish 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | tags: ["v*"] 7 | pull_request: 8 | branches: [main] 9 | 10 | jobs: 11 | test-build: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [20, 22] 17 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | persist-credentials: false 23 | 24 | - uses: pnpm/action-setup@v4 25 | with: 26 | version: latest 27 | run_install: false 28 | 29 | - name: Use Node.js ${{ matrix.node-version }} 30 | uses: actions/setup-node@v4 31 | with: 32 | node-version: ${{ matrix.node-version }} 33 | cache: "pnpm" 34 | 35 | - name: Install dependencies 36 | run: pnpm install 37 | 38 | - name: Lint 39 | working-directory: streamlit-component-lib-react-hooks 40 | run: | 41 | pnpm run check:eslint 42 | pnpm run check:prettier 43 | # - run: pnpm test 44 | # working-directory: streamlit-component-lib-react-hooks 45 | - name: Build lib 46 | run: pnpm run build 47 | working-directory: streamlit-component-lib-react-hooks 48 | 49 | - name: Test building the template 50 | run: pnpm run build 51 | working-directory: template/my_component/frontend 52 | 53 | - name: Package lib 54 | working-directory: streamlit-component-lib-react-hooks 55 | run: pnpm pack 56 | 57 | - name: Upload the built tar ball as an artifact 58 | uses: actions/upload-artifact@v4 59 | if: ${{ matrix.node-version == '22' && ! startsWith(github.ref, 'refs/tags/v') }} 60 | with: 61 | path: streamlit-component-lib-react-hooks/streamlit-component-lib-react-hooks-*.tgz 62 | name: streamlit-component-lib-react-hooks-${{ github.sha }}.tgz 63 | 64 | - name: Upload the built tar ball as an artifact (when pushed with a version tag) 65 | uses: actions/upload-artifact@v4 66 | if: ${{ matrix.node-version == '22' && startsWith(github.ref, 'refs/tags/v') }} 67 | with: 68 | path: streamlit-component-lib-react-hooks/streamlit-component-lib-react-hooks-*.tgz 69 | name: streamlit-component-lib-react-hooks-${{ github.ref_name }}.tgz 70 | 71 | publish: 72 | if: ${{ startsWith(github.ref, 'refs/tags/v') }} 73 | needs: [test-build] 74 | 75 | permissions: 76 | contents: write # Necessary for creating releases: https://github.com/softprops/action-gh-release#permissions 77 | 78 | runs-on: ubuntu-latest 79 | 80 | steps: 81 | - uses: actions/checkout@v4 82 | with: 83 | persist-credentials: false 84 | 85 | - uses: pnpm/action-setup@v4 86 | with: 87 | version: latest 88 | run_install: false 89 | 90 | - name: Use Node.js ${{ matrix.node-version }} 91 | uses: actions/setup-node@v4 92 | with: 93 | node-version: ${{ matrix.node-version }} 94 | cache: "pnpm" 95 | 96 | - uses: actions/download-artifact@v4 97 | with: 98 | name: streamlit-component-lib-react-hooks-${{ github.ref_name }}.tgz 99 | path: streamlit-component-lib-react-hooks 100 | 101 | - name: Set publishing config 102 | run: pnpm config set '//registry.npmjs.org/:_authToken' "${NODE_AUTH_TOKEN}" 103 | env: 104 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 105 | 106 | # `--no-git-checks` is required for publishing a package from a workflow triggered by a tag. 107 | # See https://github.com/pnpm/pnpm/issues/5894 108 | - run: pnpm publish streamlit-component-lib-react-hooks-*.tgz --no-git-checks --access public 109 | working-directory: streamlit-component-lib-react-hooks 110 | 111 | - name: Create a new release 112 | uses: softprops/action-gh-release@v2 113 | with: 114 | files: streamlit-component-lib-react-hooks/streamlit-component-lib-react-hooks-*.tgz 115 | generate_release_notes: true 116 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Editor directories and files 2 | .vscode/* 3 | !.vscode/extensions.json 4 | .idea 5 | .DS_Store 6 | *.suo 7 | *.ntvs* 8 | *.njsproj 9 | *.sln 10 | *.sw? 11 | 12 | 13 | ### https://raw.github.com/github/gitignore/218a941be92679ce67d0484547e3e142b2f5f6f0/Python.gitignore 14 | 15 | # Byte-compiled / optimized / DLL files 16 | __pycache__/ 17 | *.py[cod] 18 | *$py.class 19 | 20 | # C extensions 21 | *.so 22 | 23 | # Distribution / packaging 24 | .Python 25 | build/ 26 | develop-eggs/ 27 | dist/ 28 | downloads/ 29 | eggs/ 30 | .eggs/ 31 | lib/ 32 | lib64/ 33 | parts/ 34 | sdist/ 35 | var/ 36 | wheels/ 37 | share/python-wheels/ 38 | *.egg-info/ 39 | .installed.cfg 40 | *.egg 41 | MANIFEST 42 | 43 | # PyInstaller 44 | # Usually these files are written by a python script from a template 45 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 46 | *.manifest 47 | *.spec 48 | 49 | # Installer logs 50 | pip-log.txt 51 | pip-delete-this-directory.txt 52 | 53 | # Unit test / coverage reports 54 | htmlcov/ 55 | .tox/ 56 | .nox/ 57 | .coverage 58 | .coverage.* 59 | .cache 60 | nosetests.xml 61 | coverage.xml 62 | *.cover 63 | *.py,cover 64 | .hypothesis/ 65 | .pytest_cache/ 66 | cover/ 67 | 68 | # Translations 69 | *.mo 70 | *.pot 71 | 72 | # Django stuff: 73 | *.log 74 | local_settings.py 75 | db.sqlite3 76 | db.sqlite3-journal 77 | 78 | # Flask stuff: 79 | instance/ 80 | .webassets-cache 81 | 82 | # Scrapy stuff: 83 | .scrapy 84 | 85 | # Sphinx documentation 86 | docs/_build/ 87 | 88 | # PyBuilder 89 | .pybuilder/ 90 | target/ 91 | 92 | # Jupyter Notebook 93 | .ipynb_checkpoints 94 | 95 | # IPython 96 | profile_default/ 97 | ipython_config.py 98 | 99 | # pyenv 100 | # For a library or package, you might want to ignore these files since the code is 101 | # intended to run in multiple environments; otherwise, check them in: 102 | # .python-version 103 | 104 | # pipenv 105 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 106 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 107 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 108 | # install all needed dependencies. 109 | #Pipfile.lock 110 | 111 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 112 | __pypackages__/ 113 | 114 | # Celery stuff 115 | celerybeat-schedule 116 | celerybeat.pid 117 | 118 | # SageMath parsed files 119 | *.sage.py 120 | 121 | # Environments 122 | .env 123 | .venv 124 | env/ 125 | venv/ 126 | ENV/ 127 | env.bak/ 128 | venv.bak/ 129 | 130 | # Spyder project settings 131 | .spyderproject 132 | .spyproject 133 | 134 | # Rope project settings 135 | .ropeproject 136 | 137 | # mkdocs documentation 138 | /site 139 | 140 | # mypy 141 | .mypy_cache/ 142 | .dmypy.json 143 | dmypy.json 144 | 145 | # Pyre type checker 146 | .pyre/ 147 | 148 | # pytype static type analyzer 149 | .pytype/ 150 | 151 | # Cython debug symbols 152 | cython_debug/ 153 | 154 | 155 | ### https://raw.github.com/github/gitignore/218a941be92679ce67d0484547e3e142b2f5f6f0/Node.gitignore 156 | 157 | # Logs 158 | logs 159 | *.log 160 | npm-debug.log* 161 | yarn-debug.log* 162 | yarn-error.log* 163 | lerna-debug.log* 164 | 165 | # Diagnostic reports (https://nodejs.org/api/report.html) 166 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 167 | 168 | # Runtime data 169 | pids 170 | *.pid 171 | *.seed 172 | *.pid.lock 173 | 174 | # Directory for instrumented libs generated by jscoverage/JSCover 175 | lib-cov 176 | 177 | # Coverage directory used by tools like istanbul 178 | coverage 179 | *.lcov 180 | 181 | # nyc test coverage 182 | .nyc_output 183 | 184 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 185 | .grunt 186 | 187 | # Bower dependency directory (https://bower.io/) 188 | bower_components 189 | 190 | # node-waf configuration 191 | .lock-wscript 192 | 193 | # Compiled binary addons (https://nodejs.org/api/addons.html) 194 | build/Release 195 | 196 | # Dependency directories 197 | node_modules/ 198 | jspm_packages/ 199 | 200 | # Snowpack dependency directory (https://snowpack.dev/) 201 | web_modules/ 202 | 203 | # TypeScript cache 204 | *.tsbuildinfo 205 | 206 | # Optional npm cache directory 207 | .npm 208 | 209 | # Optional eslint cache 210 | .eslintcache 211 | 212 | # Microbundle cache 213 | .rpt2_cache/ 214 | .rts2_cache_cjs/ 215 | .rts2_cache_es/ 216 | .rts2_cache_umd/ 217 | 218 | # Optional REPL history 219 | .node_repl_history 220 | 221 | # Output of 'npm pack' 222 | *.tgz 223 | 224 | # Yarn Integrity file 225 | .yarn-integrity 226 | 227 | # dotenv environment variables file 228 | .env 229 | .env.test 230 | 231 | # parcel-bundler cache (https://parceljs.org/) 232 | .cache 233 | .parcel-cache 234 | 235 | # Next.js build output 236 | .next 237 | out 238 | 239 | # Nuxt.js build / generate output 240 | .nuxt 241 | dist 242 | 243 | # Gatsby files 244 | .cache/ 245 | # Comment in the public line in if your project uses Gatsby and not Next.js 246 | # https://nextjs.org/blog/next-9-1#public-directory-support 247 | # public 248 | 249 | # vuepress build output 250 | .vuepress/dist 251 | 252 | # Serverless directories 253 | .serverless/ 254 | 255 | # FuseBox cache 256 | .fusebox/ 257 | 258 | # DynamoDB Local files 259 | .dynamodb/ 260 | 261 | # TernJS port file 262 | .tern-port 263 | 264 | # Stores VSCode versions used for testing VSCode extensions 265 | .vscode-test 266 | 267 | # yarn v2 268 | .yarn/cache 269 | .yarn/unplugged 270 | .yarn/build-state.yml 271 | .yarn/install-state.gz 272 | .pnp.* 273 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm lint-staged 2 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,ts,jsx,tsx,html,css,md}": ["prettier --write"] 3 | } 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | public-hoist-pattern[]=streamlit-component-lib 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Development 2 | ## Template 3 | ```shell 4 | $ cd template 5 | $ streamlit run my_component/__init__.py 6 | ``` 7 | 8 | ```shell 9 | $ cd template/my_component/frontend 10 | $ pnpm start 11 | ``` 12 | 13 | ## Component library 14 | ```shell 15 | $ cd streamlit-component-lib-react-hooks 16 | $ pnpm build 17 | ``` 18 | 19 | # Publish 20 | ```shell 21 | $ pnpm new-version 22 | ``` 23 | 24 | Then push the commit to the GitHub repository and create a new release. The CI/CD process will release the package to NPM. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yuichiro Tachibana (Tsuchiya) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # streamlit-component-template-react-hooks 2 | 3 | [![Test streamlit-component-lib-react-hooks](https://github.com/whitphx/streamlit-component-template-react-hooks/actions/workflows/lib-test.yaml/badge.svg)](https://github.com/whitphx/streamlit-component-template-react-hooks/actions/workflows/lib-test.yaml) 4 | 5 | [![version](https://img.shields.io/npm/v/streamlit-component-lib-react-hooks)](https://www.npmjs.com/package/streamlit-component-lib-react-hooks) 6 | [![license](https://img.shields.io/npm/l/streamlit-component-lib-react-hooks)](https://www.npmjs.com/package/streamlit-component-lib-react-hooks) 7 | 8 | 9 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/D1D2ERWFG) 10 | 11 | Buy Me A Coffee 12 | 13 | [![GitHub Sponsors](https://img.shields.io/github/sponsors/whitphx?label=Sponsor%20me%20on%20GitHub%20Sponsors&style=social)](https://github.com/sponsors/whitphx) 14 | 15 | This repo contains below. 16 | * A template for creating Streamlit Components with React Hooks and functional component style: [`./template`](./template). 17 | * This is based on [the official React template](https://github.com/streamlit/component-template/tree/master/template). The original code has been copied to this repo and fixed to use React Hooks. 18 | * The source code of `streamlit-component-lib-react-hooks` npm package, which provides React-Hooks style API for Streamlit Component: [`./streamlit-component-lib-react-hooks`](streamlit-component-lib-react-hooks). 19 | * This is only for development purpose and the users of `./template` do not have to see it. 20 | 21 | ## Quickstart for the component template 22 | 23 | * Ensure you have [Python 3.6+](https://www.python.org/downloads/) and [Node.js](https://nodejs.org) installed. 24 | * Clone this repo. 25 | * Create a new Python virtual environment for the template: 26 | ``` 27 | $ cd template 28 | $ python3 -m venv venv # create venv 29 | $ . venv/bin/activate # activate venv 30 | $ pip install streamlit # install streamlit 31 | ``` 32 | * Initialize and run the component template frontend: 33 | ``` 34 | $ cd template/my_component/frontend 35 | $ yarn # Install npm dependencies. `npm install` can be used instead. 36 | $ yarn dev # Start the Vite dev server. `npm run dev` can be used instead. 37 | ``` 38 | * From a separate terminal, run the template's Streamlit app: 39 | ``` 40 | $ cd template 41 | $ . venv/bin/activate # activate the venv you created earlier 42 | $ streamlit run my_component/__init__.py # run the example 43 | ``` 44 | * If all goes well, you should see something like this: 45 | ![Quickstart Success](https://github.com/streamlit/component-template/blob/master/quickstart.png?raw=true) 46 | * Modify the frontend code at `my_component/frontend/src/MyComponent.tsx`. 47 | * Modify the Python code at `my_component/__init__.py`. 48 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "streamlit-component-lib-react-hooks", 4 | "template/my_component/frontend" 5 | ], 6 | "version": "2.1.1", 7 | "npmClient": "pnpm" 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": ">=20.5.0" 5 | }, 6 | "name": "streamlit-component-template-react-hooks", 7 | "devDependencies": { 8 | "@lerna-lite/cli": "^4.1.2", 9 | "@lerna-lite/version": "^4.1.2", 10 | "lint-staged": "^15.4.2", 11 | "husky": "^9.1.7", 12 | "prettier": "^3.5.3" 13 | }, 14 | "scripts": { 15 | "new-version": "lerna version --sync-workspace-lock --force-publish", 16 | "prepare": "husky" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "streamlit-component-lib-react-hooks" 3 | - "template/my_component/frontend" 4 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/218a941be92679ce67d0484547e3e142b2f5f6f0/Node.gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | lerna-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | .parcel-cache 80 | 81 | # Next.js build output 82 | .next 83 | out 84 | 85 | # Nuxt.js build / generate output 86 | .nuxt 87 | dist 88 | 89 | # Gatsby files 90 | .cache/ 91 | # Comment in the public line in if your project uses Gatsby and not Next.js 92 | # https://nextjs.org/blog/next-9-1#public-directory-support 93 | # public 94 | 95 | # vuepress build output 96 | .vuepress/dist 97 | 98 | # Serverless directories 99 | .serverless/ 100 | 101 | # FuseBox cache 102 | .fusebox/ 103 | 104 | # DynamoDB Local files 105 | .dynamodb/ 106 | 107 | # TernJS port file 108 | .tern-port 109 | 110 | # Stores VSCode versions used for testing VSCode extensions 111 | .vscode-test 112 | 113 | # yarn v2 114 | .yarn/cache 115 | .yarn/unplugged 116 | .yarn/build-state.yml 117 | .yarn/install-state.gz 118 | .pnp.* 119 | 120 | 121 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,jsx,ts,tsx}": "pnpm --filter streamlit-component-lib-react-hooks lint-staged:eslint", 3 | "*": "pnpm --filter streamlit-component-lib-react-hooks lint-staged:prettier" 4 | } 5 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [Unreleased] 6 | 7 | ## [2.1.1] - 2025-05-22 8 | 9 | ### Fixed 10 | 11 | - Fix `README.md` and `template/MANIFEST.in`, contributed by [@F-loat](https://github.com/F-loat). 12 | 13 | ## [2.1.0] - 2025-03-13 14 | 15 | ### Changed 16 | 17 | - Support React 19. 18 | 19 | ## [2.0.10] - 2025-03-13 20 | 21 | ### Fixed 22 | 23 | - Switch to `pnpm` from `yarn`. 24 | 25 | ## [2.0.2 - 2.0.9] 26 | 27 | Skipped. 28 | 29 | ## [2.0.1] - 2025-03-13 30 | 31 | ### Fixed 32 | 33 | - Dependencies updates. 34 | 35 | ## [2.0.0] - 2025-03-13 36 | 37 | [BREAKING]: Drop support for Node.js 18. 38 | [BREAKING]: Remove React 16. Now only React 17 and 18 are supported. 39 | [BREAKING]: `streamlit-component-lib-react-hooks` is now bundled as a dual package (CJS and ESM). 40 | 41 | ## [1.2.8] - 2024-03-04 42 | 43 | Republished the same package. 44 | 45 | ## [1.2.7] - 2024-03-04 46 | 47 | Republished the same package. 48 | 49 | ## [1.2.6] - 2024-03-03 50 | 51 | Republished the same package. 52 | 53 | ## [1.2.5] - 2024-03-03 54 | 55 | **Accidentally the versions `<=1.2.4` of this package have been unpublished and cannot be restored. The same package is now published as `@streamlit/component-lib-react-hooks` starting from version `1.2.5`.** 56 | 57 | ## [1.2.4] - 2024-02-27 58 | 59 | ### Fixed 60 | 61 | - Improve the CI/CD workflow, #114. 62 | - Remove `prepare` and `prepublishOnly` hooks. 63 | 64 | ## [1.2.3] 65 | 66 | Skipped. 67 | 68 | ## [1.2.2] 69 | 70 | Skipped. 71 | 72 | ## [1.2.1] - 2024-02-27 73 | 74 | ### Changed 75 | 76 | - Remove Node 14 and 16 from the CI/CD, #111. 77 | - Update `react-scripts` to `5.0.1` and `streamlit-component-lib` to `^2.0.0`, #112. 78 | 79 | ## [1.2.0] 80 | 81 | Skipped. 82 | 83 | ## [1.1.0] - 2023-06-08 84 | 85 | ### Changed 86 | 87 | - Remove `bootstrap.min.css`, #106. 88 | 89 | ## [1.0.2] - 2022-04-24 90 | 91 | ### Fix 92 | 93 | - Include `CHANGELOG.md` in the NPM package, #53. 94 | 95 | ## [1.0.1] - 2022-04-24 96 | 97 | ### Fix 98 | 99 | - Internal package updates. 100 | 101 | ## [1.0.0] - 2022-01-26 102 | 103 | No change - just bump the version 1.0.0 as the API has been stabilized and the package is already used in some projects stably. 104 | 105 | ## [0.3.1] - 2021-11-18 106 | 107 | ### Fix 108 | 109 | - A bug that `` does not display the caught error. 110 | 111 | ## [0.3.0] - 2021-10-24 112 | 113 | ### Added 114 | 115 | - `` as an all-in-one wrapper component for React-Hooks-based components and `useRenderData` to get the `renderData` object inside the provider, #3. 116 | 117 | ## [0.2.1] - 2021-10-24 118 | 119 | ### Added 120 | 121 | - CI/CD to test and publish, #1. 122 | - Monorepo management with Lerna, #2. 123 | 124 | ## [0.2.0] - 2021-09-20 125 | 126 | ### Fix 127 | 128 | - Depending React version. 129 | 130 | ## [0.1.0] - 2021-09-20 131 | 132 | ### Added 133 | 134 | - `useStreamlit` and `ErrorBoundary`. 135 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yuichiro Tachibana (Tsuchiya) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/README.md: -------------------------------------------------------------------------------- 1 | # Streamlit Component Library for React Hooks 2 | 3 | An npm package that provides support code for creating [Streamlit Components](https://docs.streamlit.io/en/stable/streamlit_components.html) with React and React Hooks. 4 | 5 | See https://github.com/whitphx/streamlit-component-template-react-hooks/tree/main/template for usage. 6 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import { defineConfig } from "eslint/config"; 3 | import pluginJs from "@eslint/js"; 4 | import tseslint from "typescript-eslint"; 5 | import pluginReact from "eslint-plugin-react"; 6 | import reactHooks from "eslint-plugin-react-hooks"; 7 | import reactRefresh from "eslint-plugin-react-refresh"; 8 | import pluginReactJSXRuntime from "eslint-plugin-react/configs/jsx-runtime.js"; 9 | 10 | /** @type {import('eslint').Linter.Config[]} */ 11 | export default defineConfig([ 12 | { ignores: ["dist"] }, 13 | { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] }, 14 | { languageOptions: { globals: globals.browser } }, 15 | pluginJs.configs.recommended, 16 | ...tseslint.configs.recommended, 17 | { settings: { react: { version: "detect" } } }, 18 | pluginReact.configs.flat.recommended, 19 | reactHooks.configs["recommended-latest"], 20 | reactRefresh.configs.recommended, 21 | pluginReactJSXRuntime, 22 | ]); 23 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streamlit-component-lib-react-hooks", 3 | "version": "2.1.1", 4 | "keywords": [ 5 | "streamlit" 6 | ], 7 | "type": "module", 8 | "main": "dist/cjs/index.js", 9 | "module": "dist/esm/index.js", 10 | "types": "dist/cjs/index.d.ts", 11 | "exports": { 12 | ".": { 13 | "import": "./dist/esm/index.js", 14 | "require": "./dist/cjs/index.js", 15 | "types": "./dist/cjs/index.d.ts" 16 | } 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/whitphx/streamlit-component-template-react-hooks", 21 | "directory": "streamlit-component-lib-react-hooks" 22 | }, 23 | "license": "MIT", 24 | "files": [ 25 | "dist" 26 | ], 27 | "scripts": { 28 | "build": "npm run build:cjs && npm run build:esm && npm run build:package-json", 29 | "build:cjs": "tsc -p tsconfig.cjs.json", 30 | "build:esm": "tsc -p tsconfig.esm.json", 31 | "build:package-json": "mkdir -p dist/cjs dist/esm && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json && echo '{\"type\":\"module\"}' > dist/esm/package.json", 32 | "fix:eslint": "eslint --fix 'src/**/*.{ts,tsx}'", 33 | "fix:prettier": "prettier --write .", 34 | "check:eslint": "eslint --fix 'src/**/*.{ts,tsx}'", 35 | "check:prettier": "prettier --check .", 36 | "lint-staged:eslint": "eslint --fix", 37 | "lint-staged:prettier": "prettier --write" 38 | }, 39 | "devDependencies": { 40 | "@eslint/js": "^9.22.0", 41 | "@types/react": "^19.0.10", 42 | "@types/react-dom": "^19.0.4", 43 | "eslint": "^9.22.0", 44 | "eslint-plugin-react": "^7.37.4", 45 | "eslint-plugin-react-hooks": "^5.2.0", 46 | "eslint-plugin-react-refresh": "^0.4.19", 47 | "globals": "^16.0.0", 48 | "react": "^19.0.0", 49 | "react-dom": "^19.0.0", 50 | "streamlit-component-lib": "^2.0.0", 51 | "typescript": "^5.8.2", 52 | "typescript-eslint": "^8.26.0" 53 | }, 54 | "peerDependencies": { 55 | "react": "^17.0.0 || ^18.0.0 || ^19.0.0", 56 | "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", 57 | "streamlit-component-lib": "^2.0.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/src/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface ErrorBoundaryProps { 4 | children: React.ReactNode; 5 | } 6 | interface ErrorBoundaryState { 7 | error: Error | undefined; 8 | } 9 | 10 | /** 11 | * Shows errors thrown from child components. 12 | */ 13 | class ErrorBoundary extends React.PureComponent< 14 | ErrorBoundaryProps, 15 | ErrorBoundaryState 16 | > { 17 | constructor(props: ErrorBoundaryProps) { 18 | super(props); 19 | this.state = { error: undefined }; 20 | } 21 | 22 | static getDerivedStateFromError(error: Error) { 23 | // Update state so the next render will show the fallback UI. 24 | return { error }; 25 | } 26 | 27 | render() { 28 | if (this.state.error != null) { 29 | return ( 30 |
31 |

Component Error

32 | {this.state.error.message} 33 |
34 | ); 35 | } 36 | 37 | return this.props.children; 38 | } 39 | } 40 | 41 | export default ErrorBoundary; 42 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/src/StreamlitProvider/StreamlitProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { Streamlit } from "streamlit-component-lib"; 3 | import { useNullableRenderData } from "../useNullableRenderData"; 4 | import ErrorBoundary from "../ErrorBoundary"; 5 | import { renderDataContext } from "./useRenderData"; 6 | 7 | /** 8 | * Wrapper for React-hooks-based Streamlit components. 9 | * 10 | * Bootstraps the communication interface between Streamlit and the component. 11 | */ 12 | export interface StreamlitProviderProps { 13 | children: React.ReactNode; 14 | } 15 | export function StreamlitProvider(props: StreamlitProviderProps) { 16 | const renderData = useNullableRenderData(); 17 | 18 | useEffect(() => { 19 | Streamlit.setFrameHeight(); 20 | }); 21 | 22 | // Don't render until we've gotten our first data from Streamlit. 23 | if (renderData == null) { 24 | return null; 25 | } 26 | 27 | return ( 28 | 29 | 30 | {props.children} 31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/src/StreamlitProvider/index.ts: -------------------------------------------------------------------------------- 1 | export { StreamlitProvider, StreamlitProviderProps } from "./StreamlitProvider"; 2 | export { useRenderData } from "./useRenderData"; 3 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/src/StreamlitProvider/useRenderData.ts: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { RenderData } from "streamlit-component-lib"; 3 | 4 | export const renderDataContext = React.createContext( 5 | undefined, 6 | ); 7 | 8 | /** 9 | * Returns `RenderData` received from Streamlit. 10 | */ 11 | export const useRenderData = (): RenderData => { 12 | const contextValue = useContext(renderDataContext); 13 | if (contextValue == null) { 14 | throw new Error( 15 | "useRenderData() must be used inside ", 16 | ); 17 | } 18 | 19 | return contextValue; 20 | }; 21 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/src/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-refresh/only-export-components */ 2 | export { 3 | useRenderData, 4 | StreamlitProvider, 5 | StreamlitProviderProps, 6 | } from "./StreamlitProvider"; 7 | export { useNullableRenderData } from "./useNullableRenderData"; 8 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/src/useNullableRenderData.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { Streamlit, RenderData } from "streamlit-component-lib"; 3 | 4 | /** 5 | * Returns `RenderData` received from Streamlit after the first render event received. 6 | */ 7 | export const useNullableRenderData = (): RenderData | undefined => { 8 | const [renderData, setRenderData] = useState(); 9 | 10 | useEffect(() => { 11 | const onRenderEvent = (event: Event): void => { 12 | const renderEvent = event as CustomEvent; 13 | setRenderData(renderEvent.detail); 14 | }; 15 | 16 | // Set up event listeners, and signal to Streamlit that we're ready. 17 | // We won't render the component until we receive the first RENDER_EVENT. 18 | Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRenderEvent); 19 | Streamlit.setComponentReady(); 20 | 21 | const cleanup = () => { 22 | Streamlit.events.removeEventListener( 23 | Streamlit.RENDER_EVENT, 24 | onRenderEvent, 25 | ); 26 | }; 27 | return cleanup; 28 | }, []); 29 | 30 | return renderData; 31 | }; 32 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "compilerOptions": { 4 | "target": "ESNext", 5 | "jsx": "react-jsx", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "strict": true, 11 | "skipLibCheck": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/tsconfig.cjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "outDir": "./dist/cjs", 6 | "declarationDir": "./dist/cjs" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "outDir": "./dist/esm", 6 | "declarationDir": "./dist/esm" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /streamlit-component-lib-react-hooks/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "noEmit": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /template/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yuichiro Tachibana (Tsuchiya) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | The major design pattern of this package was abstracted from Streamlit's component-template, which is subject to the same license. 25 | Here is the original copyright notice for it: 26 | 27 | Copyright (c) 2018 The Python Packaging Authority 28 | 29 | Permission is hereby granted, free of charge, to any person obtaining a copy 30 | of this software and associated documentation files (the "Software"), to deal 31 | in the Software without restriction, including without limitation the rights 32 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 33 | copies of the Software, and to permit persons to whom the Software is 34 | furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in all 37 | copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 42 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 44 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 45 | SOFTWARE. 46 | -------------------------------------------------------------------------------- /template/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include my_component/frontend/dist * 2 | -------------------------------------------------------------------------------- /template/my_component/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import streamlit.components.v1 as components 3 | 4 | # Create a _RELEASE constant. We'll set this to False while we're developing 5 | # the component, and True when we're ready to package and distribute it. 6 | # (This is, of course, optional - there are innumerable ways to manage your 7 | # release process.) 8 | _RELEASE = False 9 | 10 | # Declare a Streamlit component. `declare_component` returns a function 11 | # that is used to create instances of the component. We're naming this 12 | # function "_component_func", with an underscore prefix, because we don't want 13 | # to expose it directly to users. Instead, we will create a custom wrapper 14 | # function, below, that will serve as our component's public API. 15 | 16 | # It's worth noting that this call to `declare_component` is the 17 | # *only thing* you need to do to create the binding between Streamlit and 18 | # your component frontend. Everything else we do in this file is simply a 19 | # best practice. 20 | 21 | if not _RELEASE: 22 | _component_func = components.declare_component( 23 | # We give the component a simple, descriptive name ("my_component" 24 | # does not fit this bill, so please choose something better for your 25 | # own component :) 26 | "my_component", 27 | # Pass `url` here to tell Streamlit that the component will be served 28 | # by the local dev server that you run via `npm run start`. 29 | # (This is useful while your component is in development.) 30 | url="http://localhost:5173", 31 | ) 32 | else: 33 | # When we're distributing a production version of the component, we'll 34 | # replace the `url` param with `path`, and point it to to the component's 35 | # build directory: 36 | parent_dir = os.path.dirname(os.path.abspath(__file__)) 37 | build_dir = os.path.join(parent_dir, "frontend/dist") 38 | _component_func = components.declare_component("my_component", path=build_dir) 39 | 40 | 41 | # Create a wrapper function for the component. This is an optional 42 | # best practice - we could simply expose the component function returned by 43 | # `declare_component` and call it done. The wrapper allows us to customize 44 | # our component's API: we can pre-process its input args, post-process its 45 | # output value, and add a docstring for users. 46 | def my_component(name, key=None): 47 | """Create a new instance of "my_component". 48 | 49 | Parameters 50 | ---------- 51 | name: str 52 | The name of the thing we're saying hello to. The component will display 53 | the text "Hello, {name}!" 54 | key: str or None 55 | An optional key that uniquely identifies this component. If this is 56 | None, and the component's arguments are changed, the component will 57 | be re-mounted in the Streamlit frontend and lose its current state. 58 | 59 | Returns 60 | ------- 61 | int 62 | The number of times the component's "Click Me" button has been clicked. 63 | (This is the value passed to `Streamlit.setComponentValue` on the 64 | frontend.) 65 | 66 | """ 67 | # Call through to our private component function. Arguments we pass here 68 | # will be sent to the frontend, where they'll be available in an "args" 69 | # dictionary. 70 | # 71 | # "default" is a special argument that specifies the initial return 72 | # value of the component before the user has interacted with it. 73 | component_value = _component_func(name=name, key=key, default=0) 74 | 75 | # We could modify the value returned from the component if we wanted. 76 | # There's no need to do this in our simple example - but it's an option. 77 | return component_value 78 | 79 | 80 | # Add some test code to play with the component while it's in development. 81 | # During development, we can run this just as we would any other Streamlit 82 | # app: `$ streamlit run my_component/__init__.py` 83 | if not _RELEASE: 84 | import streamlit as st 85 | 86 | st.subheader("Component with constant args") 87 | 88 | # Create an instance of our component with a constant `name` arg, and 89 | # print its output value. 90 | num_clicks = my_component("World") 91 | st.markdown("You've clicked %s times!" % int(num_clicks)) 92 | 93 | st.markdown("---") 94 | st.subheader("Component with variable args") 95 | 96 | # Create a second instance of our component whose `name` arg will vary 97 | # based on a text_input widget. 98 | # 99 | # We use the special "key" argument to assign a fixed identity to this 100 | # component instance. By default, when a component's arguments change, 101 | # it is considered a new instance and will be re-mounted on the frontend 102 | # and lose its current state. In this case, we want to vary the component's 103 | # "name" argument without having it get recreated. 104 | name_input = st.text_input("Enter a name", value="Streamlit") 105 | num_clicks = my_component(name_input, key="foo") 106 | st.markdown("You've clicked %s times!" % int(num_clicks)) 107 | -------------------------------------------------------------------------------- /template/my_component/frontend/eslint.config.js: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import { defineConfig } from "eslint/config"; 3 | import pluginJs from "@eslint/js"; 4 | import tseslint from "typescript-eslint"; 5 | import pluginReact from "eslint-plugin-react"; 6 | import reactHooks from "eslint-plugin-react-hooks"; 7 | import reactRefresh from "eslint-plugin-react-refresh"; 8 | import pluginReactJSXRuntime from "eslint-plugin-react/configs/jsx-runtime.js"; 9 | 10 | /** @type {import('eslint').Linter.Config[]} */ 11 | export default defineConfig([ 12 | { ignores: ["dist"] }, 13 | { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] }, 14 | { languageOptions: { globals: globals.browser } }, 15 | pluginJs.configs.recommended, 16 | ...tseslint.configs.recommended, 17 | { settings: { react: { version: "detect" } } }, 18 | pluginReact.configs.flat.recommended, 19 | reactHooks.configs["recommended-latest"], 20 | reactRefresh.configs.recommended, 21 | pluginReactJSXRuntime, 22 | ]); 23 | -------------------------------------------------------------------------------- /template/my_component/frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Streamlit Component 5 | 6 | 7 | 8 | 9 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /template/my_component/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streamlit_component_template", 3 | "version": "2.1.1", 4 | "private": true, 5 | "type": "module", 6 | "dependencies": { 7 | "react": "^19.0.0", 8 | "react-dom": "^19.0.0", 9 | "streamlit-component-lib": "^2.0.0", 10 | "streamlit-component-lib-react-hooks": "workspace:*" 11 | }, 12 | "devDependencies": { 13 | "@eslint/js": "^9.22.0", 14 | "@types/react": "^19.0.10", 15 | "@types/react-dom": "^19.0.4", 16 | "@vitejs/plugin-react": "^4.3.4", 17 | "eslint": "^9.22.0", 18 | "eslint-plugin-react": "^7.37.4", 19 | "eslint-plugin-react-hooks": "^5.2.0", 20 | "eslint-plugin-react-refresh": "^0.4.19", 21 | "globals": "^16.0.0", 22 | "typescript": "^5.8.2", 23 | "typescript-eslint": "^8.26.0", 24 | "vite": "^6.2.1" 25 | }, 26 | "scripts": { 27 | "dev": "vite", 28 | "build": "vite build", 29 | "lint": "eslint ." 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /template/my_component/frontend/src/MyComponent.tsx: -------------------------------------------------------------------------------- 1 | import { Streamlit } from "streamlit-component-lib" 2 | import { useRenderData } from "streamlit-component-lib-react-hooks" 3 | import React, { useState, useCallback } from "react" 4 | 5 | /** 6 | * This is a React-based component template with functional component and hooks. 7 | */ 8 | function MyComponent() { 9 | // "useRenderData" returns the renderData passed from Python. 10 | const renderData = useRenderData() 11 | 12 | const [numClicks, setNumClicks] = useState(0) 13 | const [isFocused, setIsFocused] = useState(false) 14 | 15 | /** Click handler for our "Click Me!" button. */ 16 | const onClicked = useCallback(() => { 17 | // Increment `numClicks` state, and pass the new value back to 18 | // Streamlit via `Streamlit.setComponentValue`. 19 | const newValue = numClicks + 1 20 | setNumClicks(newValue) 21 | Streamlit.setComponentValue(newValue) 22 | }, [numClicks]) 23 | 24 | /** Focus handler for our "Click Me!" button. */ 25 | const onFocus = useCallback(() => { 26 | setIsFocused(true) 27 | }, []) 28 | 29 | /** Blur handler for our "Click Me!" button. */ 30 | const onBlur = useCallback(() => { 31 | setIsFocused(false) 32 | }, []) 33 | 34 | // Arguments that are passed to the plugin in Python are accessible 35 | // via `renderData.args`. Here, we access the "name" arg. 36 | const name = renderData.args["name"] 37 | 38 | // Streamlit sends us a theme object via renderData that we can use to ensure 39 | // that our component has visuals that match the active theme in a 40 | // streamlit app. 41 | const theme = renderData.theme 42 | const style: React.CSSProperties = {} 43 | 44 | // Maintain compatibility with older versions of Streamlit that don't send 45 | // a theme object. 46 | if (theme) { 47 | // Use the theme object to style our button border. Alternatively, the 48 | // theme style is defined in CSS vars. 49 | const borderStyling = `1px solid ${isFocused ? theme.primaryColor : "gray"}` 50 | style.border = borderStyling 51 | style.outline = borderStyling 52 | } 53 | 54 | // Show a button and some text. 55 | // When the button is clicked, we'll increment our "numClicks" state 56 | // variable, and send its new value back to Streamlit, where it'll 57 | // be available to the Python program. 58 | return ( 59 | 60 | Hello, {name}!   61 | 70 | 71 | ) 72 | } 73 | 74 | export default MyComponent 75 | -------------------------------------------------------------------------------- /template/my_component/frontend/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { createRoot } from "react-dom/client"; 3 | import { StreamlitProvider } from "streamlit-component-lib-react-hooks"; 4 | import MyComponent from "./MyComponent"; 5 | 6 | createRoot(document.getElementById("root")!).render( 7 | 8 | 9 | 10 | 11 | , 12 | ); 13 | -------------------------------------------------------------------------------- /template/my_component/frontend/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "Bundler", 12 | "allowImportingTsExtensions": true, 13 | "isolatedModules": true, 14 | "moduleDetection": "force", 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | "esModuleInterop": true, 18 | 19 | /* Linting */ 20 | "strict": true, 21 | "noUnusedLocals": true, 22 | "noUnusedParameters": true, 23 | "noFallthroughCasesInSwitch": true, 24 | "noUncheckedSideEffectImports": true 25 | }, 26 | "include": ["src"] 27 | } 28 | -------------------------------------------------------------------------------- /template/my_component/frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /template/my_component/frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "isolatedModules": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "noUncheckedSideEffectImports": true 22 | }, 23 | "include": ["vite.config.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /template/my_component/frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | export default defineConfig({ 5 | base: "./", 6 | plugins: [react()], 7 | }); 8 | -------------------------------------------------------------------------------- /template/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | setuptools.setup( 4 | name="streamlit-custom-component", 5 | version="0.0.1", 6 | author="", 7 | author_email="", 8 | description="", 9 | long_description="", 10 | long_description_content_type="text/plain", 11 | url="", 12 | packages=setuptools.find_packages(), 13 | include_package_data=True, 14 | classifiers=[], 15 | python_requires=">=3.6", 16 | install_requires=[ 17 | # By definition, a Custom Component depends on Streamlit. 18 | # If your component has other Python dependencies, list 19 | # them here. 20 | "streamlit >= 0.63", 21 | ], 22 | ) 23 | --------------------------------------------------------------------------------