Skip to main content

Streamlit custom component for rendering and editing schema graphs

Project description

streamlit-schema-editor

Streamlit custom component for viewing and editing schema, ER, and source-to-target mapping graphs with React Flow and Streamlit Custom Components v2.

Installation

uv pip install streamlit-schema-editor

What It Supports

  • schema viewing with table and column metadata
  • source-to-target mapping with interactive relationship creation and deletion
  • inline column editing for name and data_type
  • optional edge action button for relationship inspection or custom workflows
  • read-only or editable canvases
  • validation styling for tables, columns, and relationships
  • metadata passthrough on tables, columns, and relationships
  • structured event + event_context payloads for Streamlit-side workflows

Usage

import streamlit as st

from streamlit_schema_editor import streamlit_schema_editor


if "tables" not in st.session_state:
    st.session_state.tables = [
        {
            "id": "crm_customer",
            "label": "crm_customer",
            "metadata": {"system": "crm"},
            "columns": [
                {"id": "customer_id", "name": "customer_id", "data_type": "uuid"},
                {"id": "cust_name", "name": "cust_name", "data_type": "varchar"},
            ],
        },
        {
            "id": "dim_customer",
            "label": "dim_customer",
            "metadata": {"system": "warehouse"},
            "columns": [
                {"id": "customer_key", "name": "customer_key", "data_type": "bigint"},
                {"id": "customer_name", "name": "customer_name", "data_type": "varchar"},
            ],
        },
    ]

if "relationships" not in st.session_state:
    st.session_state.relationships = [
        {
            "id": "rel::crm_customer::cust_name::dim_customer::customer_name",
            "source_table": "crm_customer",
            "source_column": "cust_name",
            "target_table": "dim_customer",
            "target_column": "customer_name",
            "validation": {
                "status": "loading",
                "summary": "Awaiting warehouse validation for this mapping.",
            },
            "metadata": {
                "rule_id": "MAP-1024",
                "sql_expression": "UPPER(TRIM(cust_name))",
            },
        }
    ]

value = streamlit_schema_editor(
    st.session_state.tables,
    st.session_state.relationships,
    height=700,
    show_controls=True,
    max_connections_per_handle=2,
    key="schema-editor",
)

st.session_state.tables = value["tables"]
st.session_state.relationships = value["relationships"]

st.write(value["event"])
st.json(value["event_context"])

API

streamlit_schema_editor(
    tables,
    relationships,
    *,
    groups=None,
    height=600,
    fit_view=True,
    editable=True,
    connectable=None,
    draggable=None,
    deletable=None,
    show_controls=False,
    show_arrowheads=True,
    show_edge_button=False,
    show_column_count_badge=True,
    show_groups=True,
    group_layout="manual",
    group_order=None,
    table_layout_within_group="manual",
    show_validation=True,
    validation_refresh_key=None,
    column_type_options=None,
    allow_zoom=True,
    allow_duplicate_edges=False,
    max_connections_per_handle=None,
    max_incoming_connections_per_handle=None,
    max_outgoing_connections_per_handle=None,
    key=None,
)

editable acts as the default for connectable and deletable. Dragging is enabled by default even for read-only canvases, and editable also enables inline column editing, row add/remove controls, and column edit events. Override the interaction flags individually when you need a mixed mode.

Use show_arrowheads=False for ER-style views where you want the canvas to read more like an undirected diagram, and show_column_count_badge=False when long table names need the extra header space. The legacy aliases max_incoming_per_target and max_outgoing_per_source are still accepted for backward compatibility, but the generic connection-limit parameters are the preferred public API.

show_edge_button is optional and defaults to False. Turn it on only when you want a per-relationship action affordance in the middle of the edge.

Use groups plus per-table group_id when you want optional labeled containers such as Source / Target, database lanes, or bronze/silver/gold layers. show_groups controls whether those containers are currently rendered without requiring you to change the underlying table or relationship payload.

Use group_layout="columns" or group_layout="rows" when you want the component to place groups automatically without manually setting every group position. Use group_order to control that automatic order, and table_layout_within_group="stack" to vertically arrange grouped tables for lane-style views. Automatic layout modes intentionally own placement, so they are best paired with read-only or lightly interactive canvases.

show_validation controls whether validation-derived colors, badges, and summaries are rendered. If you want to force those visuals to refresh without changing the schema payload, change validation_refresh_key between reruns.

Edge Button

Enable the button when you want relationship inspection or a custom workflow trigger from the canvas:

value = streamlit_schema_editor(
    tables,
    relationships,
    show_edge_button=True,
    key="schema-editor-with-edge-actions",
)

if value["event"] == "edge_details_requested":
    relationship_id = (value["event_context"] or {}).get("relationship_id")
    st.write(f"Inspect relationship: {relationship_id}")

When enabled, the edge button can show:

  • relationship["label"] when present
  • i as a compact fallback for inspection

This button is useful for cases like opening a details panel, surfacing lineage metadata, showing validation context, or launching a relationship editor. For pure ER or schema-viewing use cases, leave it disabled.

Return Value

  • tables: current table list with updated positions
  • groups: current group list with updated positions and sizes
  • relationships: current relationships after connect/delete actions
  • selection: current selected table, column, and relationship ids
  • event: semantic event name or None
  • event_context: structured payload for the last event

Event Names

  • selection_changed
  • node_moved
  • table_deleted
  • column_created
  • column_updated
  • column_deleted
  • relationship_created
  • relationship_deleted
  • relationship_rejected
  • edge_details_requested

edge_details_requested is only emitted when show_edge_button=True.

Schema Fields

Tables, columns, and relationships support generic metadata passthrough. Tables, columns, and relationships can also opt into a top-level validation object. Tables can optionally declare a single group_id, and groups are defined separately with id, label, position, width, height, and metadata:

{
    "validation": {
        "status": "error",
        "code": "missing_column",
        "summary": "Column not found in upstream schema.",
        "detail": "Latest introspection did not return crm_customer.region_code.",
    }
}

Use validation for generic UI state that the component understands, and keep app-specific semantics in metadata. For example, SQL expressions, lineage attributes, ownership, or workflow IDs should live in metadata, not as first-class component fields.

Inline Column Editing

When editable=True, table nodes support lightweight inline schema editing:

  • click a column name to edit column["name"]
  • click a data type to edit column["data_type"]
  • use the placeholder row at the bottom of a table to add a column row
  • use the × control on a row to remove that column

Column ids remain stable and are not edited inline. If you delete a column, any attached relationships are removed from the graph at the same time.

If you pass column_type_options, the inline data-type editor uses an in-node combobox with suggested values while still allowing custom typed entries such as varchar(255).

Common Recipes

Viewer-style canvas with dragging enabled but editing disabled:

value = streamlit_schema_editor(
    tables,
    relationships,
    editable=False,
    draggable=True,
    connectable=False,
    deletable=False,
    show_controls=True,
    key="schema-viewer",
)

Mapping editor with inline column editing and relationship inspection:

value = streamlit_schema_editor(
    tables,
    relationships,
    editable=True,
    show_edge_button=True,
    show_controls=True,
    key="mapping-editor",
)

Toggle labeled source / target lanes on and off at runtime:

groups = [
    {"id": "source", "label": "Source", "width": 420, "height": 640},
    {"id": "target", "label": "Target", "width": 380, "height": 640},
]

tables = [
    {**source_table, "group_id": "source"},
    {**target_table, "group_id": "target"},
]

show_groups = st.toggle("Show groups", value=True)

value = streamlit_schema_editor(
    tables,
    relationships,
    groups=groups,
    show_groups=show_groups,
    group_layout="columns",
    group_order=["source", "target"],
    table_layout_within_group="stack",
    key="schema-editor-groups",
)

Hide validation visuals until the user opts in:

show_validation = st.toggle("Show validation", value=True)

value = streamlit_schema_editor(
    tables,
    relationships,
    show_validation=show_validation,
    key="schema-editor-validation-toggle",
)

Force validation visuals to refresh on demand:

if "validation_refresh_nonce" not in st.session_state:
    st.session_state.validation_refresh_nonce = 0

if st.button("Refresh validation visuals"):
    st.session_state.validation_refresh_nonce += 1

value = streamlit_schema_editor(
    tables,
    relationships,
    show_validation=True,
    validation_refresh_key=st.session_state.validation_refresh_nonce,
    key="schema-editor-validation-refresh",
)

Use known type options while still allowing custom values:

value = streamlit_schema_editor(
    tables,
    relationships,
    editable=True,
    column_type_options=[
        "uuid",
        "bigint",
        "integer",
        "varchar",
        "text",
        "timestamp",
        "json",
    ],
    key="schema-editor-type-options",
)

Handle inline column edit events in Streamlit:

value = streamlit_schema_editor(
    tables,
    relationships,
    editable=True,
    key="schema-editor-events",
)

if value["event"] == "column_created":
    st.success(f"Added column: {(value['event_context'] or {}).get('column_id')}")

if value["event"] == "column_updated":
    context = value["event_context"] or {}
    st.info(
        "Updated "
        f"{context.get('table_id')}.{context.get('column_id')} "
        f"fields={context.get('fields')}"
    )

if value["event"] == "column_deleted":
    context = value["event_context"] or {}
    st.warning(
        "Deleted "
        f"{context.get('table_id')}.{context.get('column_id')} "
        f"and removed relationships={context.get('deleted_relationship_ids')}"
    )

Examples

Run the examples from the project root:

uv run streamlit run example.py
uv run streamlit run examples/playground.py
uv run streamlit run examples/schema_viewer.py
uv run streamlit run examples/er_diagram.py
uv run streamlit run examples/databricks_mapping.py
  • example.py: full source-to-target mapping demo
  • examples/playground.py: interactive playground for toggling runtime options, grouping visibility, and validation live
  • examples/schema_viewer.py: read-only schema browser
  • examples/er_diagram.py: ER-style relationship view with arrowheads hidden
  • examples/databricks_mapping.py: Databricks-inspired source-to-target mapping demo with labeled group lanes

Development

Build the frontend first, then build the Python package:

cd streamlit_schema_editor/frontend
npm install
npm run build
cd ../..
uv build

Testing

Run the Python tests:

uv run pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

streamlit_schema_editor-0.1.0.tar.gz (218.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

streamlit_schema_editor-0.1.0-py3-none-any.whl (166.6 kB view details)

Uploaded Python 3

File details

Details for the file streamlit_schema_editor-0.1.0.tar.gz.

File metadata

  • Download URL: streamlit_schema_editor-0.1.0.tar.gz
  • Upload date:
  • Size: 218.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for streamlit_schema_editor-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f6ee4b921fa948477ccab17512da2e8f978972fc4c512624d9193dc083891a6a
MD5 598ab72911dec50194125609078cc3f7
BLAKE2b-256 d9f0a29759982e15d6b4a4e3211b4daa230eb0a97341c2c2fc0c6c51287732bc

See more details on using hashes here.

File details

Details for the file streamlit_schema_editor-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: streamlit_schema_editor-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 166.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for streamlit_schema_editor-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 662cd43fc861860d54e3d71b8fcc357a726653b61915ce10f7f63c62f43a1b70
MD5 943784066990b2adc8048a6d68575a0a
BLAKE2b-256 73615decb7cff7f39d76b0b1b903a290beced6447ef2c544877e146b8d6fe6d5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page