Skip to main content

Overview

Functional component wrapper used by the component renderer. A component keeps hook state, tracks observable argument dependencies, renders a function body into controls, and schedules updates/effects through the active page session. Source: flet.components.component:60
Components enable a reactive, functional programming model in Flet similar to React hooks. They allow you to build reusable UI pieces with encapsulated state and lifecycle.

Class Signature

from flet.components import Component

@control("C")
class Component(BaseControl):
    """Functional component wrapper used by the component renderer."""

Creating Components

Use the @component decorator to create functional components:
from flet import component
import flet as ft

@component
def counter_component():
    count, set_count = ft.use_state(0)
    
    def increment(e):
        set_count(count + 1)
    
    return ft.Column([
        ft.Text(f"Count: {count}"),
        ft.ElevatedButton("Increment", on_click=increment)
    ])

Internal Structure

The following properties are internal to the component system. Do not modify them directly.

Component State

fn
Callable[..., Any]
The component function being wrapped.Source: component.py:70Metadata: skip=True
args
tuple[Any, ...]
Positional arguments passed to the component function.Source: component.py:71Metadata: skip=True
kwargs
dict[str, Any]
Keyword arguments passed to the component function.Source: component.py:72Metadata: skip=True
memoized
bool
default:"False"
Whether this component is memoized (only re-renders when props change).Source: component.py:80Metadata: skip=True
_state
_ComponentState
Internal mutable state backing the component instance.Source: component.py:76Stores:
  • Hook registry and cursor
  • Memoization snapshots
  • Mount flags
  • Observable subscriptions
Metadata: skip=True
_b
Any
The rendered body (output) of the component.Source: component.py:83

Methods

Lifecycle Methods

update
() -> None
Render component body and patch changed output to the session.Source: component.py:98This method:
  1. Resets hook cursor
  2. Detaches old observable subscriptions
  3. Subscribes to new observables
  4. Renders the component function
  5. Freezes the output
  6. Memoizes if needed
  7. Patches changes to session
  8. Runs render effects
# Typically called automatically by the framework
component.update()
before_update
() -> None
Called before the component is updated.Source: component.py:144Checks memoization and re-renders if:
  • Component is dirty (state changed)
  • Not memoized
  • Props (args/kwargs) have changed
did_mount
() -> None
Mark component mounted and run mount-time effects.Source: component.py:329Called after component is added to the page tree. Triggers all effect hooks for initial execution.
will_unmount
() -> None
Mark component unmounted, detach observable listeners, and run cleanups.Source: component.py:338Called before component is removed from the page tree. Executes cleanup functions from all effect hooks.

Hook Management

use_hook
(default: Callable[[], HookTypeT]) -> HookTypeT
Return hook instance for current render slot, creating it if missing.Source: component.py:258Parameters:
  • default (Callable): Factory function to initialize a new hook
Returns: Hook instance bound to the current hook cursor positionThis is the internal mechanism used by all use_* hooks like use_state, use_effect, etc.

Internal Update Scheduling

_schedule_update
() -> None
Mark component dirty and enqueue a session update.Source: component.py:181Called when component state changes (e.g., from set_state in use_state).
_schedule_effect
(hook: EffectHook, is_cleanup: bool = False) -> None
Enqueue effect or effect-cleanup execution in session scheduler.Source: component.py:190Parameters:
  • hook (EffectHook): Effect hook to execute
  • is_cleanup (bool): Whether to run hook cleanup instead of effect body

Observable Subscriptions

_subscribe_observable_args
(args: tuple[Any, ...], kwargs: dict[str, Any]) -> None
Attach subscriptions for observable positional/keyword arguments.Source: component.py:202Automatically subscribes to any Observable objects passed as props.
_attach_observable_subscription
(observable: Observable) -> ObservableSubscription
Subscribe component updates to an observable argument.Source: component.py:218Parameters:
  • observable (Observable): Observable object to subscribe to
Returns: Created observable subscription
_detach_observable_subscriptions
() -> None
Dispose and clear all observable subscriptions for this component.Source: component.py:249

Component State (_ComponentState)

Internal state structure for components:
@dataclass
class _ComponentState:
    hooks: list[Hook] = field(default_factory=list)
    hook_cursor: int = 0
    mounted: bool = False
    is_dirty: bool = False
    observable_subscriptions: list[ObservableSubscription] = field(default_factory=list)
    last_args: tuple[Any, ...] = field(default_factory=tuple)
    last_kwargs: dict[str, Any] = field(default_factory=dict)
    last_b: Any = None

Renderer

The Renderer class coordinates functional component rendering and context stacks. Source: component.py:362

Renderer Methods

render
(root_fn: Callable[..., Any], *args, **kwargs) -> Any
Render a root callable within this renderer context/frame.Source: component.py:427Parameters:
  • root_fn: Callable producing component output
  • *args: Positional arguments forwarded to root_fn
  • **kwargs: Keyword arguments forwarded to root_fn
Returns: Value returned by root_fn
render_component
(fn: Callable[..., Any], args: tuple[Any, ...], kwargs: dict[str, Any], key=None) -> Component
Create a frozen Component wrapper for a function call.Source: component.py:444Parameters:
  • fn: Component function decorated with @component
  • args: Positional arguments for the component function
  • kwargs: Keyword arguments for the component function
  • key: Optional identity key
Returns: Component wrapper ready for control lifecycleRaises: ValueError if fn is not marked as a component
set_memo
() -> None
Mark next rendered component as memoized.Source: component.py:378
push_context
(key: object, value: object) -> None
Push a context value for the given key.Source: component.py:385Parameters:
  • key: Context identity token
  • value: Context value to make active
pop_context
(key: object) -> None
Pop current context value for key and remove empty stacks.Source: component.py:397Parameters:
  • key: Context identity token

Usage Examples

Basic Component

import flet as ft
from flet import component

@component
def greeting(name: str):
    return ft.Text(f"Hello, {name}!", size=24)

def main(page: ft.Page):
    page.add(
        greeting("Alice"),
        greeting("Bob")
    )

ft.run(main)

Stateful Component with Hooks

import flet as ft
from flet import component

@component
def todo_list():
    todos, set_todos = ft.use_state([])
    input_ref = ft.use_ref()
    
    def add_todo(e):
        if input_ref.current.value:
            set_todos(todos + [input_ref.current.value])
            input_ref.current.value = ""
            page.update()
    
    return ft.Column([
        ft.Row([
            ft.TextField(
                ref=input_ref,
                hint_text="Enter todo",
                expand=True
            ),
            ft.IconButton(
                icon=ft.Icons.ADD,
                on_click=add_todo
            )
        ]),
        ft.Column([
            ft.Text(todo) for todo in todos
        ])
    ])

def main(page: ft.Page):
    page.add(todo_list())

ft.run(main)

Memoized Component

import flet as ft
from flet import component, memo

@memo
@component
def expensive_component(data: list):
    # This only re-renders when 'data' changes
    processed = [item.upper() for item in data]
    return ft.Column([
        ft.Text(item) for item in processed
    ])

def main(page: ft.Page):
    data = ["apple", "banana", "cherry"]
    page.add(expensive_component(data))

ft.run(main)

Component with Effects

import flet as ft
from flet import component
import asyncio

@component
def timer_component():
    count, set_count = ft.use_state(0)
    
    async def increment_timer():
        while True:
            await asyncio.sleep(1)
            set_count(count + 1)
    
    # Start timer on mount
    ft.use_effect(
        lambda: page.run_task(increment_timer),
        deps=[]  # Empty deps = run once on mount
    )
    
    return ft.Text(f"Elapsed: {count}s", size=30)

def main(page: ft.Page):
    page.add(timer_component())

ft.run(main)

Component with Observable Props

import flet as ft
from flet import component
from flet.components import Observable

@component
def display_component(value: Observable):
    # Automatically re-renders when observable changes
    return ft.Text(f"Value: {value}", size=24)

def main(page: ft.Page):
    observable_value = Observable(0)
    
    def increment(e):
        observable_value.set(observable_value.get() + 1)
    
    page.add(
        display_component(observable_value),
        ft.ElevatedButton(
            "Increment",
            on_click=increment
        )
    )

ft.run(main)

Component Lifecycle

  1. Creation: Component function is called with props (args/kwargs)
  2. Initialization: Hooks are initialized on first render
  3. Mounting: did_mount() is called, mount effects run
  4. Rendering: Component function executes, returns controls
  5. Updating: When state/props change, component re-renders
  6. Unmounting: will_unmount() is called, cleanup functions run

Best Practices

  1. Hook Order: Always call hooks in the same order on every render
  2. Memoization: Use @memo for expensive components that don’t need frequent updates
  3. Effects: Use empty dependency array [] for mount-only effects
  4. Cleanup: Return cleanup functions from effects to prevent memory leaks
  5. Props: Keep props immutable; use observables for reactive data

See Also

Build docs developers (and LLMs) love