Back to Streamlit

Ccv2 Troubleshooting

lib/streamlit/.agents/skills/developing-with-streamlit/references/ccv2-troubleshooting.md

1.57.1.dev202605187.0 KB
Original Source

Troubleshooting CCv2 components

Contents

  • v1 contamination (most common failure)
  • Packaged assets and manifests (asset_dir, component key)
  • Renaming / placeholder drift
  • Inline strings vs file-backed assets (path heuristic)
  • Globs (0 matches or multiple matches)
  • Defaults, callbacks, and missing result attributes
  • Keys (Python key= vs frontend key)
  • Shadow DOM / isolate_styles surprises
  • Frontend build (Vite) gotchas
  • DOM clobbering (overwriting injected HTML/CSS)

v1 contamination (most common failure)

The single most common cause of broken CCv2 components is accidentally mixing in v1 APIs from outdated examples, blog posts, or training data. v1 is deprecated and will not work.

Symptoms:

  • Component renders as blank/empty iframe
  • Streamlit is not defined errors in browser console
  • setComponentValue is not a function errors
  • Component never communicates back to Python

Check your code for these banned patterns and replace them:

Banned (v1)Correct (v2)
st.components.v1st.components.v2.component(...)
components.declare_component()st.components.v2.component(...)
components.html()st.components.v2.component(...) with html=
Streamlit.setComponentValue(val)setStateValue("key", val) or setTriggerValue(...)
Streamlit.setFrameHeight()Remove entirely (v2 handles sizing)
Streamlit.setComponentReady()Remove entirely (v2 has no ready signal)
window.StreamlitUse destructured args from export default function
window.parent.postMessage(...)Use setStateValue / setTriggerValue
streamlit-component-lib (npm)@streamlit/component-v2-lib (if types needed)
function sendMessageToStreamlitClientRemove entirely; use v2 callback args

Fix: search all .py and .js/.ts/.tsx files for these patterns. If any are found, rewrite using the v2 equivalents above. The cookiecutter template generates correct v2 code — if you started from the template, the contamination is in your customizations.

Packaged assets and manifests (asset_dir, component key)

“Component '<name>' must be declared in pyproject.toml with asset_dir to use file-backed js/css.”

You passed a path-like js=/css= string (like index-*.js or assets/index-*.js) but Streamlit can’t find an asset_dir for this component key.

Fix:

  • If you want inline JS/CSS, pass a multi-line string with the actual code (not a path).
  • If you want packaged assets, ensure:
    • Your wheel includes a pyproject.toml with [[tool.streamlit.component.components]] ... asset_dir = ...
    • You call st.components.v2.component("<project>.<component>", js="...", css="...") with the matching fully-qualified key.

Important context:

  • This error is expected if you test packaged wrappers via plain Python import in some environments.
  • Prefer streamlit run ... for packaged verification because manifest discovery is part of Streamlit runtime initialization.

Inline strings vs file-backed assets (path heuristic)

CCv2 uses a heuristic: strings that “look like” paths are treated as file references. A multi-line string is always treated as inline content.

Fix:

  • Prefer triple-quoted multi-line strings for inline html/css/js.
  • Avoid single-line minified JS/CSS in js=/css=; add a newline if you must.

Globs (0 matches or multiple matches)

Globs must match exactly one file under asset_dir.

Fix:

  • Clean the build output directory before rebuilding.
  • Make your bundler output a predictable index-<hash>.js / index-<hash>.css (or assets/index-<hash>... if you emit into an assets/ subdir).
  • If you started from Streamlit’s component-template, run npm run clean from your frontend/ directory to clear the build/ output so index-*.js matches exactly one file.

Renamed project/package still shows old template names

Symptoms:

  • Old names like streamlit-component-x / streamlit_component_x remain in paths or metadata.
  • Imports, manifest component keys, and registration keys no longer align.

Fix:

  • Rename/update all related surfaces together:
    • root pyproject.toml project name
    • import package folder and MANIFEST.in paths
    • [tool.setuptools.packages.find] and [tool.setuptools.package-data]
    • in-package manifest (<import_name>/pyproject.toml)
    • wrapper registration key ("<project.name>.<component.name>")
    • README/example imports and install commands
  • Rebuild frontend and reinstall editable package after rename.

Defaults, callbacks, and missing result attributes

default={...} doesn’t apply / missing result attributes

Defaults only apply to state keys, and Streamlit expects those keys to be declared via on_<key>_change callback parameters at mount time.

Fix:

  • If you pass default={"value": ...}, also pass on_value_change=lambda: None.
  • For triggers, don’t expect defaults; triggers are transient and default to None.

Keys (Python key= vs frontend key)

  • Python key= is the user-visible Streamlit element key.
  • The frontend also receives a key string that is generated by Streamlit and is not the same as the Python key unless you explicitly pass the user key through data.

Fix:

  • If your frontend needs a stable identifier, pass it in data={"user_key": key, ...}.

Shadow DOM / isolate_styles surprises

Shadow DOM (isolate_styles=True) surprises

With isolate_styles=True (default):

  • Your component is mounted in a shadow root.
  • parentElement is a ShadowRoot.
  • Global CSS (like Tailwind injected into the document) won’t automatically style your component.

Fix:

  • Keep isolate_styles=True for safety and use CSS variables and component-local styles.
  • Use isolate_styles=False only when you intentionally need global styling behavior.

Frontend build gotchas (Vite)

If you deviate from the template’s Vite config (or you’re wiring Vite into an existing repo), these are the common footguns:

  • Missing base: "./": relative asset URLs can break when served from Streamlit’s component URL path.
  • Stale build artifacts: Vite outputs hashed filenames; if you keep old builds around, index-*.js can match multiple files. Clean the build dir before rebuilding.

DOM clobbering (overwriting injected HTML/CSS)

Accidentally overwriting your component DOM

If you directly set parentElement.innerHTML = ..., you can overwrite the HTML/CSS that Streamlit injected from your html=/css= arguments.

Fix:

  • Prefer querySelector + modifying children.
  • If you need dynamic HTML, create a new child element and set that element’s innerHTML.