├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── my_component ├── __init__.py └── frontend │ ├── .browserslistrc │ ├── .env │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierrc │ ├── babel.config.js │ ├── package-lock.json │ ├── package.json │ ├── public │ └── index.html │ ├── src │ ├── App.vue │ ├── MyComponent.vue │ ├── main.ts │ ├── shims-vue.d.ts │ └── streamlit │ │ ├── StreamlitVue.ts │ │ ├── WithStreamlitConnection.vue │ │ └── index.ts │ ├── tsconfig.json │ └── vue.config.js ├── quickstart.png └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Python - https://github.com/github/gitignore/blob/master/Python.gitignore 3 | ######################################################################## 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # Distribution / packaging 10 | build/ 11 | dist/ 12 | eggs/ 13 | .eggs/ 14 | *.egg-info/ 15 | *.egg 16 | 17 | # Unit test / coverage reports 18 | .coverage 19 | .coverage\.* 20 | .pytest_cache/ 21 | .mypy_cache/ 22 | test-reports 23 | 24 | # Test fixtures 25 | cffi_bin 26 | 27 | # Pyenv Stuff 28 | .python-version 29 | 30 | ######################################################################## 31 | # OSX - https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 32 | ######################################################################## 33 | .DS_Store 34 | .DocumentRevisions-V100 35 | .fseventsd 36 | .Spotlight-V100 37 | .TemporaryItems 38 | .Trashes 39 | .VolumeIcon.icns 40 | .com.apple.timemachine.donotpresent 41 | 42 | ######################################################################## 43 | # node - https://github.com/github/gitignore/blob/master/Node.gitignore 44 | ######################################################################## 45 | # Logs 46 | npm-debug.log* 47 | yarn-debug.log* 48 | yarn-error.log* 49 | 50 | # Dependency directories 51 | node_modules/ 52 | 53 | # Coverage directory used by tools like istanbul 54 | coverage/ 55 | 56 | # Lockfiles 57 | yarn.lock 58 | package-lock.json 59 | 60 | ######################################################################## 61 | # JetBrains 62 | ######################################################################## 63 | .idea 64 | 65 | ######################################################################## 66 | # VSCode 67 | ######################################################################## 68 | .vscode/ 69 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The Python Packaging Authority 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include package/frontend/build * 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Streamlit Component template in Vue.js 2 | 3 | Vue 3 template to build a Streamlit component. Uses Vue.js scoped slot to send parameters from Streamlit Python script into `args` props of your component. 4 | 5 | ## Setup 6 | 7 | [Copy paste](https://github.com/streamlit/component-template#quickstart) of the original component-template quickstart. 8 | 9 | - Ensure you have [Python 3.6+](https://www.python.org/downloads/), [Node.js](https://nodejs.org), and [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) installed. 10 | - Clone this repo. 11 | - Create a new Python virtual environment for the template: 12 | 13 | ``` 14 | $ python3 -m venv venv # create venv 15 | $ . venv/bin/activate # activate venv 16 | $ pip install streamlit # install streamlit 17 | ``` 18 | 19 | - Initialize and run the component template frontend: 20 | 21 | ``` 22 | $ cd my_component/frontend 23 | $ npm install # Install npm dependencies 24 | $ npm run serve # Start the Webpack dev server 25 | ``` 26 | 27 | - From a separate terminal, run the template's Streamlit app: 28 | 29 | ``` 30 | $ . venv/bin/activate # activate the venv you created earlier 31 | $ streamlit run my_component/__init__.py # run the example 32 | ``` 33 | 34 | - If all goes well, you should see something like this: 35 |  36 | - Modify the frontend code at `my_component/frontend/src/MyComponent.vue`. 37 | - Parameters passed by Python script are made available in `args` props. 38 | - Modify the Python code at `my_component/__init__.py`. 39 | - Feel free to rename the `my_component` folder, `MyComponent.vue` file with its import in `App.vue`, and package name in `setup.py` and `package.json`. 40 | 41 | ## Resources 42 | 43 | - [Higher Order Components in Vue.js](https://medium.com/bethink-pl/higher-order-components-in-vue-js-a79951ac9176) 44 | - [Do we need Higher Order Components in Vue.js?](https://medium.com/bethink-pl/do-we-need-higher-order-components-in-vue-js-87c0aa608f48) 45 | - [Build better higher-order components with Vue 3](https://blog.logrocket.com/build-better-higher-order-components-with-vue-3/) 46 | - [Scoped slots](https://v3.vuejs.org/guide/component-slots.html#scoped-slots) 47 | - [Using Slots in Vue.js](https://www.smashingmagazine.com/2019/07/using-slots-vue-js/) 48 | - [Single File Components](https://v3.vuejs.org/guide/single-file-component.html) 49 | - [SFC Spec](https://vue-loader.vuejs.org/spec.html) 50 | -------------------------------------------------------------------------------- /my_component/__init__.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import streamlit.components.v1 as components 3 | 4 | import os 5 | 6 | _RELEASE = False 7 | 8 | # Declare a Streamlit component. `declare_component` returns a function 9 | # that is used to create instances of the component. We're naming this 10 | # function "_component_func", with an underscore prefix, because we don't want 11 | # to expose it directly to users. Instead, we will create a custom wrapper 12 | # function, below, that will serve as our component's public API. 13 | 14 | # It's worth noting that this call to `declare_component` is the 15 | # *only thing* you need to do to create the binding between Streamlit and 16 | # your component frontend. Everything else we do in this file is simply a 17 | # best practice. 18 | 19 | if not _RELEASE: 20 | _component_func = components.declare_component( 21 | "my_component", 22 | url="http://localhost:3001", 23 | ) 24 | else: 25 | parent_dir = os.path.dirname(os.path.abspath(__file__)) 26 | build_dir = os.path.join(parent_dir, "frontend/dist") 27 | _component_func = components.declare_component( 28 | "my_component", path=build_dir) 29 | 30 | # Create a wrapper function for the component. This is an optional 31 | # best practice - we could simply expose the component function returned by 32 | # `declare_component` and call it done. The wrapper allows us to customize 33 | # our component's API: we can pre-process its input args, post-process its 34 | # output value, and add a docstring for users. 35 | 36 | 37 | def my_component(name, key=None): 38 | component_value = _component_func(name=name, key=key, default=0) 39 | return component_value 40 | 41 | 42 | st.subheader("Component with constant args") 43 | 44 | # Create an instance of our component with a constant `name` arg, and 45 | # print its output value. 46 | num_clicks = my_component("World") 47 | st.markdown("You've clicked %s times!" % int(num_clicks)) 48 | 49 | st.markdown("---") 50 | st.subheader("Component with variable args") 51 | 52 | # Create a second instance of our component whose `name` arg will vary 53 | # based on a text_input widget. 54 | # 55 | # We use the special "key" argument to assign a fixed identity to this 56 | # component instance. By default, when a component's arguments change, 57 | # it is considered a new instance and will be re-mounted on the frontend 58 | # and lose its current state. In this case, we want to vary the component's 59 | # "name" argument without having it get recreated. 60 | name_input = st.text_input("Enter a name", value="Streamlit") 61 | num_clicks = my_component(name_input, key="foo") 62 | st.markdown("You've clicked %s times!" % int(num_clicks)) 63 | -------------------------------------------------------------------------------- /my_component/frontend/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /my_component/frontend/.env: -------------------------------------------------------------------------------- 1 | # Run the component's dev server on :3001 2 | # (The Streamlit dev server already runs on :3000) 3 | PORT=3001 4 | 5 | # Don't automatically open the web browser on `npm run start`. 6 | BROWSER=none -------------------------------------------------------------------------------- /my_component/frontend/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | "plugin:@typescript-eslint/eslint-recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | 'eslint:recommended', 11 | '@vue/typescript/recommended' 12 | ], 13 | parserOptions: { 14 | ecmaVersion: 2020 15 | }, 16 | rules: { 17 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 18 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /my_component/frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /my_component/frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "lf", 3 | "semi": false, 4 | "trailingComma": "es5" 5 | } 6 | -------------------------------------------------------------------------------- /my_component/frontend/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /my_component/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streamlit_component_template", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "streamlit-component-lib": "^1.1.1", 13 | "vue": "^3.0.0-0" 14 | }, 15 | "devDependencies": { 16 | "@typescript-eslint/eslint-plugin": "^2.33.0", 17 | "@typescript-eslint/parser": "^2.33.0", 18 | "@vue/cli-plugin-babel": "~4.5.0", 19 | "@vue/cli-plugin-eslint": "~4.5.0", 20 | "@vue/cli-plugin-typescript": "^4.5.0", 21 | "@vue/cli-service": "~4.5.0", 22 | "@vue/compiler-sfc": "^3.0.0-0", 23 | "@vue/eslint-config-typescript": "^5.0.2", 24 | "babel-eslint": "^10.1.0", 25 | "eslint": "^6.7.2", 26 | "eslint-plugin-vue": "^7.0.0-0", 27 | "typescript": "~3.9.3" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /my_component/frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |