Skip to content

Latest commit

 

History

History
182 lines (137 loc) · 5.91 KB

File metadata and controls

182 lines (137 loc) · 5.91 KB

Component System

The component system bridges React components to Python with auto-generated wrappers.

Generation Pipeline

React/TypeScript Source
        ↓
   extract-meta.js (Node.js)
   ├── react-docgen (for .js/.jsx - parses PropTypes)
   └── TypeScript Compiler API (for .tsx - parses type definitions)
        ↓
   metadata.json
        ↓
   dash-generate-components (Python CLI)
        ↓
   Python component classes (+ R/Julia if requested)

Key Files

  • dash/extract-meta.js - Node.js script that extracts component metadata. For JavaScript components, uses react-docgen to parse PropTypes. For TypeScript components, uses the TypeScript Compiler API to parse type definitions and convert them to metadata format.

  • dash/development/component_generator.py - CLI entry point (dash-generate-components). Orchestrates metadata extraction and code generation.

  • dash/development/_py_components_generation.py - Generates Python class files from metadata. Creates typed __init__ methods, docstrings, and prop validation.

  • dash/development/_py_prop_typing.py - Maps JavaScript/TypeScript types to Python types (e.g., arrayOftyping.Sequence, shapeTypedDict).

  • dash/development/_generate_prop_types.py - For TypeScript components, generates a proptypes.js file since TSX doesn't have runtime PropTypes.

Component JSON Structure

Components serialize to {type, namespace, props} via to_plotly_json() in base_component.py:

# Python component
html.Div(id='my-div', children='Hello')

# Serializes to JSON
{
    "type": "Div",
    "namespace": "dash_html_components",
    "props": {
        "id": "my-div",
        "children": "Hello"
    }
}

This JSON is sent to the frontend via /_dash-layout (initial load) and /_dash-update-component (callback responses).

Frontend Component Resolution

Components must be available on the frontend at window[namespace][type]:

// Component packages register themselves
window.dash_html_components = {
    Div: DivComponent,
    Span: SpanComponent,
    // ...
};

window.dash_core_components = {
    Dropdown: DropdownComponent,
    Graph: GraphComponent,
    // ...
};

The renderer resolves components via registry.js:

resolve: (component) => {
    const {type, namespace} = component;
    return window[namespace][type];  // Returns React component class
}

Package Structure

_imports_.py

Auto-generated by generate_imports() in _py_components_generation.py:

from .Dropdown import Dropdown
from .Graph import Graph
from .Input import Input
# ... one import per component

__all__ = [
    "Dropdown",
    "Graph",
    "Input",
    # ...
]

__init__.py

Manually maintained, imports from _imports_.py:

from ._imports_ import *  # Re-exports all components
from ._imports_ import __all__ as _components

# Read version from package-info.json
with open(os.path.join(_basepath, "package-info.json")) as f:
    package = json.load(f)
__version__ = package["version"]

# Define JavaScript assets to serve
_js_dist = [
    {
        "relative_package_path": "dash_core_components.js",
        "namespace": "dash",
    },
    # async chunks, source maps, proptypes.js for dev, etc.
]

# Attach _js_dist to each component class
for _component in _components:
    setattr(locals()[_component], "_js_dist", _js_dist)

Resource System (_js_dist / _css_dist)

dash/resources.py manages JavaScript and CSS asset loading for components.

Resource Entry Structure

{
    "relative_package_path": "dcc/dash_core_components.js",  # Path within package
    "external_url": "https://unpkg.com/...",                 # CDN fallback
    "namespace": "dash",                                      # JS namespace
    "async": True | "eager" | "lazy",                        # Async loading mode
    "dynamic": True,                                          # Loaded on demand (source maps)
    "dev_package_path": "dcc/proptypes.js",                  # Dev-only path
    "dev_only": True,                                         # Only in dev mode
}

Resource Loading Flow

  1. Each component class has _js_dist (and optionally _css_dist) attribute set in __init__.py
  2. When component is imported, ComponentMeta registers module in ComponentRegistry.registry
  3. ComponentRegistry.get_resources("_js_dist") iterates registered modules, collects all _js_dist lists
  4. Scripts / Css classes in resources.py filter resources based on config:
    • serve_locally=True: Use relative_package_path, serve via /_dash-component-suites/
    • serve_locally=False: Use external_url (CDN)
    • eager_loading=True: Load async resources immediately
    • dev_bundles=True: Include dev_package_path resources

Async Loading Modes

  • async: True - Dynamic unless eager_loading is enabled
  • async: "lazy" - Always loaded dynamically (on-demand)
  • async: "eager" - Loaded dynamically only if server isn't in eager mode

Creating New Components

  1. Write React component with PropTypes (JS) or TypeScript props interface (TSX)
  2. Run dash-generate-components src/lib/components -p package_name
  3. Generated Python wrapper goes to package_name/ComponentName.py
  4. _imports_.py is auto-generated with imports for all components
  5. For TSX, proptypes.js is also generated for runtime prop validation
  6. Bundle with webpack, register on window[namespace]
  7. Update __init__.py to set _js_dist on components

Built-in Component Packages

Managed as a Lerna monorepo in components/:

  • components/dash-core-components/ - Interactive components (Dropdown, Slider, Graph, Input, etc.)
  • components/dash-html-components/ - HTML element wrappers (Div, Span, H1, etc.)
  • components/dash-table/ - DataTable component (deprecated in favor of dash-ag-grid)

Use dash-update-components "component-name" to rebuild after changes.