2. Components and props

A page can be broken into reusable Component classes. Let’s show some guestbook entries, each in its own card:

guestbook.py
"""Tutorial step 2: a component with props."""

from dataclasses import dataclass

from rational import App, Card, Column, Component, Container, Heading, Page, Text

app = App(title="Guestbook")


@dataclass(frozen=True)
class Entry:
    name: str
    message: str


ENTRIES = [
    Entry("Ada", "What a lovely place!"),
    Entry("Grace", "Signed, sealed, compiled."),
]


class EntryCard(Component):
    entry: Entry  # an annotated class attribute is a prop: EntryCard(entry=...)

    def render(self):
        return Card(
            Text(self.entry.message),
            Text(f"— {self.entry.name}", muted=True, size="sm"),
        )


@app.page("/")
class Home(Page):
    title = "Guestbook"

    def render(self):
        return Container(
            Heading("Guestbook", level=1),
            Column(*[EntryCard(entry=entry, key=i) for i, entry in enumerate(ENTRIES)]),
            max_width=640,
        )


if __name__ == "__main__":
    app.run()

Props

Annotated class attributes are props, the inputs of a component. entry: Entry makes EntryCard(entry=...) required; a default value (excited: bool = False) makes a prop optional. A misspelled or missing prop is an error when the component is created, not a silent bug.

Keys

key= gives each card a stable identity among its siblings. It doesn’t matter yet, but it will in the next step, once the cards have state of their own. When you render a list, always pass a key.

Primitives you will use most

  • Text: Heading, Text, Link, Badge

  • Layout: Container, Row, Column, Grid, Card

  • Forms: Form, Input, Textarea, Select, Checkbox, Button

They are listed in the API reference. For anything else, h("tag", ...) builds any element.

Next: 3. State, events and forms.