streamlit-shadcn-ui: shadcn components inside Streamlit, without an iframe
Using shadcn-ui components in streamlit
At a glance
- What is it?
- Version 1.0 rebuilt the package on Streamlit Components V2, so Select, Popover and Date Picker render in the page instead of a clipped frame. Here is what the install looks like, where the API breaks from 0.1.x, and who should stay put.
- Who is it for?
- Adopt streamlit-shadcn-ui if you are starting a Streamlit app on Streamlit 1.60 or newer and want shadcn's palette, radius and focus rings rather than Streamlit's own widgets; the pip install is one line and the quick-start example runs as written.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem: Streamlit widgets that look like Streamlit widgets
Streamlit ships a fixed widget set with its own visual language. If you want a date picker that opens a calendar panel, a hover card, an OTP input, or a button with shadcn's variant styling, the framework does not give you one, and hand-rolling it in HTML means losing Streamlit's state handling. streamlit-shadcn-ui fills that gap by exposing shadcn/ui components as Python functions that return Python values. The audience is Streamlit developers who already like the Streamlit execution model and only want a different component layer on top of it, not a different framework. The README is explicit about the boundary: shadcn owns the component palette, radius, typography, focus rings and interaction styling, while Streamlit provides the surrounding light/dark color scheme, language and direction. The package does not restyle shadcn to look like Streamlit, so an app built with it will not match the default Streamlit look.
Components V2 and the Shadow DOM: why overlays stopped being clipped
The 1.0 release is V2-only. Components are implemented on Streamlit Components V2 and render without an iframe, which is the single most consequential change in the project's history. The README states that anchored and modal overlays stay in their own component ShadowRoot and use the browser top layer, so Select, Dropdown Menu, Popover, Hover Card, Date Picker and Alert Dialog are not clipped by the Streamlit layout. That clipping problem is the classic failure of iframe-based component wrappers, and it is why a dropdown near the bottom of a page can render off-screen in older approaches.
The data flow is deliberately shallow. The README gives the production path as Python API, then the Streamlit Components V2 adapter, then an owned generated shadcn component, then a Base UI behavior primitive. The stack is checked-in shadcn source backed by Base UI, React 19, Tailwind CSS 4, and Streamlit's isolated Shadow DOM runtime. Two rendering paths exist. Ordinary helper calls are independently isolated V2 components, while ui.elements is the opt-in aggregate path that renders one complete typed tree in a single V2 root. Neither path accepts native Streamlit elements as React children, which is a real constraint rather than a documentation gap.
Install and a first working component
The package installs from PyPI and requires Python 3.10 or newer plus Streamlit 1.60 or newer. The README gives a single install command, and pyproject.toml confirms the only runtime dependency is streamlit>=1.60.
pip install streamlit-shadcn-uiThe quick-start example builds a select, a switch and a button, then writes their returned values. Choice components return the original Python values rather than their display labels, so fruit below holds "Banana" and not a label object.
import streamlit as st
import streamlit_shadcn_ui as ui
fruit = ui.select(
"Fruit",
["Apple", "Banana", "Orange"],
value="Banana",
)
enabled = ui.switch("Enable notifications", value=True)
if ui.button("Save", variant="default"):
st.write({"fruit": fruit, "enabled": enabled})Run the file with streamlit run and you should see the three components laid out in the app, with the select showing Banana selected. The README notes that key is optional for ordinary calls, and that you should add a stable key when components are created from a loop, can be reordered, or need identity that survives changes to their other arguments. That is the pattern to reach for in list rendering.
for project in projects:
ui.checkbox(project.name, key=f"project_{project.id}")The README points to docs/components/elements.md, the V2 technical assessment, and the changelog for release changes. The bundled documentation app uses Home.py as an explicit router, with a product homepage, an interactive Playground, and component pages that are executable documentation for the catalog.
The 0.1.x migration is a rewrite, not a version bump
The 1.0 package root is the V2 API. The V1 iframe implementation and the streamlit_shadcn_ui.v1 compatibility namespace are not shipped, and the README says plainly that applications which cannot migrate yet should remain on the last 0.1.x release. That is an unusually blunt upgrade note and it is worth taking at face value.
The listed source changes are mechanical but spread across a codebase: button(text=...) becomes button(label=...); grouped V1 checkboxes become ordinary composition of scalar checkboxes; with ui.card(...) becomes Streamlit layout around a declarative Card; low-level trigger and content helpers and experimental element() trees are removed; and component return values and callbacks replace reliance on raw session-state transport dictionaries. The last item is the one that tends to hurt, because code that read a transport dictionary out of session state has no direct equivalent and must be rewritten around return values. The full mapping lives in docs/v2-compatibility-matrix.md, and the 1.0 API decision is recorded in docs/adr/011-v2-1.0-python-api.md. If you have a large 0.1.x app, budget the migration as a project rather than a dependency bump.
Where the abstraction stops: composition limits and the precomposed helpers
The precomposed helpers such as Card, Popover and Collapsible accept documented text or data arguments. They are not containers for arbitrary Streamlit widgets. If your design has a shadcn Card wrapping a st.dataframe, or a Popover containing a native Streamlit form, the package does not support that, and the README states the reason: neither rendering path accepts native Streamlit elements as React children. You either build the interior from the package's own input, choice and action nodes through ui.elements, or you keep Streamlit layout around a declarative Card.
That makes ui.elements the interesting part of the API and also the part with the steepest learning curve, since it is a typed tree rather than a context manager over Streamlit widgets. The README's own example nests el.card, el.card_header, el.card_content and el.card_footer inside one ui.elements block, then reads email.value and save.clicked afterwards. Note that the values are read off the returned node objects, not from session state, which is consistent with the rest of the V2 design. The package is also the wrong tool if you want Streamlit's native look. The README is direct that it does not restyle shadcn to resemble Streamlit, so adopting it is a visual commitment, not a drop-in theme.
Alternatives: shadcn/ui on the web, and Streamlit's own component ecosystem
The closest alternative for the styling itself is shadcn/ui used directly in a React application. The difference in approach is total: shadcn/ui gives you source files you own inside a web app, with the full React ecosystem available for state and routing, while streamlit-shadcn-ui keeps the Streamlit server-side execution model and exposes only a fixed catalog of components as Python functions. If your team already writes React and does not need Streamlit's rerun model, going straight to shadcn/ui removes an entire adapter layer and the version coupling to Streamlit 1.60. The trade-off is that you then own the backend and the deployment story that Streamlit was handling for you.
Within Streamlit itself, the native widget set is the other alternative, and the honest comparison is that native widgets compose freely with each other and with st.dataframe, st.form and the rest of the framework. streamlit-shadcn-ui buys appearance and specific interaction patterns at the cost of that free composition. For a dashboard where the widgets are mostly inputs feeding charts, native Streamlit is usually the lower-risk choice. The package earns its place when the overlay behavior matters, for example a Date Picker or Alert Dialog that must not be clipped by the surrounding layout.
Maintenance, licence and what a version pin commits you to
The repository is not archived, and the last push was on 2026-08-09, which is recent. Releases are frequent: 1.0.0 and 1.0.1 both landed on 2026-08-01, and v1.1.0 on 2026-08-09. The version string in pyproject.toml reads 1.4.0, so the release cadence has continued past the changelog entries listed here. Nothing in the repository describes a support window or a deprecation policy for the V2 API, so treat the API as moving and pin a version in production.
The licence is MIT, declared in both the README and pyproject.toml, with license-files pointing at LICENSE. MIT is permissive and places few obligations on how you redistribute the package; it also means there is no commercial support contract attached to it. The upgrade cost is dominated by the Streamlit floor. Because the dependency is streamlit>=1.60 and the whole component path is built on Components V2, an app that cannot move to Streamlit 1.60 cannot use 1.0 or later at all. That coupling, not the Python API, is the constraint that will decide your upgrade timing. The development scripts in the repository, such as ./scripts/verify_v2_release_source.sh and python3 -m pytest tests/v2 -q, are for contributors building the frontend rather than for consumers.
Editorial conclusion
Adopt streamlit-shadcn-ui if you are starting a Streamlit app on Streamlit 1.60 or newer and want shadcn's palette, radius and focus rings rather than Streamlit's own widgets; the pip install is one line and the quick-start example runs as written. Do not adopt it if your app is pinned below Streamlit 1.60, if you are still on the 0.1.x API and cannot rewrite button(text=...) calls and grouped checkboxes, or if you need native Streamlit elements nested as children of a shadcn Card, which neither the helper path nor ui.elements accepts. Verify first that your environment meets Python 3.10 and Streamlit 1.60, then read docs/v2-compatibility-matrix.md against your own call sites before you upgrade, because the V1 iframe implementation and the streamlit_shadcn_ui.v1 namespace are not shipped in 1.0 and later.
Frequently asked questions
What is streamlit-shadcn-ui?
It is a Python package that exposes shadcn/ui components to Streamlit, implemented on Streamlit Components V2. Components render without an iframe, and the README states that shadcn owns the palette, radius, typography and focus rings while Streamlit provides the surrounding color scheme and direction.
Does streamlit-shadcn-ui work with Streamlit 1.59 or older?
No. The README requires Streamlit 1.60 or newer, and the 1.0 release is V2-only, built on Streamlit Components V2. Applications that cannot move to that Streamlit version should remain on the last 0.1.x release.
Can I nest native Streamlit widgets inside a shadcn Card in streamlit-shadcn-ui?
No. The README states that neither the ordinary helper path nor ui.elements accepts native Streamlit elements as React children. Precomposed helpers such as Card, Popover and Collapsible accept documented text or data arguments instead.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/observedobserver-streamlit-shadcn-ui)