Skip to content

Nesting

PyJinHx makes it easy to compose components together. You can nest single components, lists of components, or dictionaries of components.

Two nesting styles, two id rules

There are two ways to nest:

  • Python field values (this page) — you build child instances yourself; give each child an explicit id (auto-generated pjx-<n> ids are not stable hooks).
  • PascalCase <Tag/> in templates (see PascalCase Tags) — the renderer instantiates children for you and can auto-generate the id when auto_id=True (the default).

Direct Nesting

Pass a component as a field value:

from pyjinhx import BaseComponent


class Button(BaseComponent):
    id: str
    text: str


class Card(BaseComponent):
    id: str
    title: str
    action: Button  # Nested component
<!-- card.html -->
<div id="{{ id }}" class="card">
    <h2>{{ title }}</h2>
    <div class="actions">
        {{ action }}
    </div>
</div>
card = Card(
    id="hero",
    title="Welcome",
    action=Button(id="cta", text="Get Started")
)
html = card.render()

Accessing Nested Component Properties

Nested components are wrapped in NestedComponentWrapper, giving you access to both the rendered HTML and the original props:

<!-- card.html -->
<div id="{{ id }}" class="card">
    <h2>{{ title }}</h2>

    <!-- Render the component -->
    {{ action }}

    <!-- Access component properties -->
    <p>Button text: {{ action.props.text }}</p>
    <p>Button ID: {{ action.props.id }}</p>
</div>

How it works

When a field value is a BaseComponent, PyJinHx wraps it in a NestedComponentWrapper before rendering. Use {{ field }} to output the HTML, and {{ field.props.X }} to access the original component's properties. This also applies to components inside lists and dicts.

Lists of Components

Use a list to render multiple components:

class ButtonGroup(BaseComponent):
    id: str
    buttons: list[Button]
<!-- button_group.html -->
<div id="{{ id }}" class="button-group">
    {% for button in buttons %}
        {{ button }}
    {% endfor %}
</div>
group = ButtonGroup(
    id="actions",
    buttons=[
        Button(id="save", text="Save"),
        Button(id="cancel", text="Cancel"),
        Button(id="delete", text="Delete"),
    ]
)

Accessing List Item Properties

<div id="{{ id }}" class="button-group">
    {% for button in buttons %}
        <div class="button-wrapper" data-text="{{ button.props.text }}">
            {{ button }}
        </div>
    {% endfor %}
</div>

Mixed Collections

Combine different types in lists and dicts:

class Widget(BaseComponent):
    id: str
    content: str


class Container(BaseComponent):
    id: str
    items: list[Button | Card | Widget]
{% for item in items %}
    <div class="item">{{ item }}</div>
{% endfor %}

Dictionaries of Components

Use dictionaries for named component collections:

class Dashboard(BaseComponent):
    id: str
    widgets: dict[str, Widget]
<!-- dashboard.html -->
<div id="{{ id }}" class="dashboard">
    <aside>{{ widgets.sidebar }}</aside>
    <main>{{ widgets.main }}</main>
    <footer>{{ widgets.footer }}</footer>
</div>
dashboard = Dashboard(
    id="main",
    widgets={
        "sidebar": Widget(id="nav", content="Navigation"),
        "main": Widget(id="content", content="Main content"),
        "footer": Widget(id="foot", content="Footer"),
    }
)

Deep Nesting

Components can be nested to any depth. Reusing the Button and Card classes from above:

class Page(BaseComponent):
    id: str
    title: str
    main_card: Card
page = Page(
    id="home",
    title="Welcome",
    main_card=Card(
        id="hero",
        title="Get Started",
        action=Button(id="cta", text="Sign Up")
    )
)

html = page.render()

The rendering happens recursively - nested components are rendered before their parents.