Documentation Index
Fetch the complete documentation index at: https://mintlify.com/flet-dev/flet/llms.txt
Use this file to discover all available pages before exploring further.
Hooks are functions that let you use state and other component features in functional components. They enable you to write clean, reusable logic without classes.
Rules of Hooks
Before diving in, understand these critical rules:
- Only call hooks at the top level - Don’t call hooks inside loops, conditions, or nested functions
- Only call hooks in components - Hooks must be called inside functions decorated with
@ft.component
- Call hooks in the same order - Hook calls must happen in the same order on every render
Why? Hooks rely on call order to maintain state across renders. Breaking these rules causes bugs.
Location: flet/components/component.py:258
use_state
Adds state to a component. Returns a tuple of (value, setter).
Location: flet/components/hooks/use_state.py:47
Signature
def use_state(
initial: StateT | Callable[[], StateT],
) -> tuple[StateT, Callable[[StateT | Updater], None]]
Basic Usage
import flet as ft
@ft.component
def Counter():
count, set_count = ft.use_state(0)
return ft.Column([
ft.Text(f"Count: {count}"),
ft.ElevatedButton(
"Increment",
on_click=lambda _: set_count(count + 1)
)
])
Lazy Initialization
Pass a function to compute the initial state only once:
@ft.component
def ExpensiveComponent():
# expensive_computation() is only called on first render
data, set_data = ft.use_state(lambda: expensive_computation())
return ft.Text(str(data))
Functional Updates
The setter accepts an updater function that receives the previous state:
@ft.component
def Counter():
count, set_count = ft.use_state(0)
def increment():
# Use functional update to ensure you have the latest value
set_count(lambda prev: prev + 1)
return ft.ElevatedButton("Increment", on_click=lambda _: increment())
Observable State
State values can be Observable objects for shared state:
from flet.components.observable import Observable
shared_counter = Observable(0)
@ft.component
def DisplayCounter():
count, set_count = ft.use_state(shared_counter)
return ft.Text(f"Shared count: {count.value}")
Location: flet/components/hooks/use_state.py:71
use_effect
Perform side effects in components. Effects run after render and can optionally return a cleanup function.
Location: flet/components/hooks/use_effect.py:41
Signature
def use_effect(
setup: Callable[[], Any | Awaitable[Any]],
dependencies: Sequence[Any] | None = None,
cleanup: Callable[[], Any | Awaitable[Any]] | None = None,
)
Run on Every Render
@ft.component
def Logger():
count, set_count = ft.use_state(0)
ft.use_effect(
lambda: print(f"Count changed to: {count}"),
dependencies=None # Runs every render
)
return ft.ElevatedButton("Increment", on_click=lambda _: set_count(count + 1))
Run Once on Mount
@ft.component
def DataLoader():
data, set_data = ft.use_state(None)
async def fetch_data():
# Simulated API call
await asyncio.sleep(1)
set_data({"message": "Hello from API"})
# Empty dependencies = run once on mount
ft.use_effect(
lambda: asyncio.create_task(fetch_data()),
dependencies=[]
)
return ft.Text(str(data))
Run When Dependencies Change
@ft.component
def SearchResults(query: str):
results, set_results = ft.use_state([])
async def search():
# Fetch results for query
new_results = await api.search(query)
set_results(new_results)
# Re-run effect when query changes
ft.use_effect(
lambda: asyncio.create_task(search()),
dependencies=[query]
)
return ft.Column([ft.Text(r) for r in results])
Cleanup Functions
@ft.component
def Timer():
seconds, set_seconds = ft.use_state(0)
def setup_timer():
# Start a timer
task = asyncio.create_task(tick())
async def tick():
while True:
await asyncio.sleep(1)
set_seconds(lambda s: s + 1)
# Cleanup: cancel task when component unmounts
def cleanup():
task.cancel()
return cleanup
ft.use_effect(setup_timer, dependencies=[])
return ft.Text(f"Elapsed: {seconds}s")
Alternatively, use the cleanup parameter:
ft.use_effect(
setup=lambda: start_timer(),
cleanup=lambda: stop_timer(),
dependencies=[]
)
Location: flet/components/hooks/use_effect.py:11
Lifecycle Helpers
Flet provides convenience functions for common lifecycle patterns:
on_mounted
Runs exactly once after the component mounts:
Location: flet/components/hooks/use_effect.py:77
@ft.component
def MyComponent():
ft.on_mounted(lambda: print("Component mounted!"))
return ft.Text("Hello")
on_unmounted
Runs exactly once when the component unmounts:
Location: flet/components/hooks/use_effect.py:88
@ft.component
def MyComponent():
ft.on_unmounted(lambda: print("Cleaning up!"))
return ft.Text("Hello")
on_updated
Runs after each post-mount render:
Location: flet/components/hooks/use_effect.py:100
@ft.component
def MyComponent():
count, set_count = ft.use_state(0)
# Runs after every update
ft.on_updated(lambda: print(f"Updated! Count is {count}"))
# Or with dependencies
ft.on_updated(
lambda: print(f"Count changed to {count}"),
dependencies=[count]
)
return ft.ElevatedButton("Increment", on_click=lambda _: set_count(count + 1))
use_ref
Persist a mutable value across renders without triggering updates.
Location: flet/components/hooks/use_ref.py:37
Signature
def use_ref(
initial_value: RefValueT | Callable[[], RefValueT] | None = None,
) -> MutableRef[RefValueT]
Basic Usage
@ft.component
def InputFocus():
input_ref = ft.use_ref()
def focus_input(_):
if input_ref.current:
input_ref.current.focus()
return ft.Column([
ft.TextField(ref=input_ref),
ft.ElevatedButton("Focus Input", on_click=focus_input)
])
Storing Mutable Values
Unlike state, updating .current doesn’t trigger a re-render:
@ft.component
def RenderCounter():
render_count = ft.use_ref(0)
# Increment on every render without causing infinite loop
render_count.current += 1
return ft.Text(f"This component rendered {render_count.current} times")
Storing Previous Values
@ft.component
def CompareToPrevious(value: int):
prev_ref = ft.use_ref(value)
result = f"Current: {value}, Previous: {prev_ref.current}"
# Update ref after render
ft.use_effect(lambda: setattr(prev_ref, 'current', value), [value])
return ft.Text(result)
Location: flet/components/hooks/use_ref.py:15
use_memo
Memoize expensive computations to avoid recalculating on every render.
Location: flet/components/hooks/use_memo.py:24
Signature
def use_memo(
calculate_value: Callable[[], MemoValueT],
dependencies: Sequence[Any] | None = None
) -> MemoValueT
Basic Usage
@ft.component
def FilteredList(items: list, filter_text: str):
# Only recalculate when items or filter_text changes
filtered = ft.use_memo(
lambda: [item for item in items if filter_text.lower() in item.lower()],
dependencies=[items, filter_text]
)
return ft.Column([ft.Text(item) for item in filtered])
Expensive Calculations
@ft.component
def DataProcessor(data: list):
# This expensive operation only runs when data changes
processed = ft.use_memo(
lambda: expensive_data_processing(data),
dependencies=[data]
)
return ft.Text(f"Processed {len(processed)} items")
Always Recompute
@ft.component
def RandomNumber():
# Recompute on every render
random_num = ft.use_memo(
lambda: random.randint(1, 100),
dependencies=None
)
return ft.Text(f"Random: {random_num}")
Location: flet/components/hooks/use_memo.py:11
use_callback
Memoize function identity to prevent unnecessary re-renders of child components.
Location: flet/components/hooks/use_callback.py:12
Signature
def use_callback(
fn: Callable[P, R],
dependencies: Sequence[Any] | None = None,
) -> Callable[P, R]
Basic Usage
@ft.component
def ParentComponent():
count, set_count = ft.use_state(0)
# Memoize callback to prevent child re-renders
handle_click = ft.use_callback(
lambda: set_count(count + 1),
dependencies=[count]
)
return ft.Column([
ft.Text(f"Count: {count}"),
ChildButton(on_click=handle_click)
])
@ft.component
def ChildButton(on_click):
return ft.ElevatedButton("Increment", on_click=on_click)
With Multiple Dependencies
@ft.component
def Form():
name, set_name = ft.use_state("")
email, set_email = ft.use_state("")
submit = ft.use_callback(
lambda: api.submit({"name": name, "email": email}),
dependencies=[name, email]
)
return ft.Column([
ft.TextField(value=name, on_change=lambda e: set_name(e.control.value)),
ft.TextField(value=email, on_change=lambda e: set_email(e.control.value)),
ft.ElevatedButton("Submit", on_click=lambda _: submit())
])
use_context
Access shared context values without prop drilling.
Location: flet/components/hooks/use_context.py:88
Creating Context
Location: flet/components/hooks/use_context.py:54
# Create a theme context
ThemeContext = ft.create_context(default_value="light")
Providing Context
@ft.component
def App():
theme, set_theme = ft.use_state("light")
def toggle_theme():
set_theme("dark" if theme == "light" else "light")
# Provide theme to all children
return ThemeContext(
theme,
lambda: ft.Column([
ft.ElevatedButton("Toggle Theme", on_click=lambda _: toggle_theme()),
ThemedComponent(),
])
)
Consuming Context
@ft.component
def ThemedComponent():
theme = ft.use_context(ThemeContext)
bg_color = ft.Colors.WHITE if theme == "light" else ft.Colors.BLACK
text_color = ft.Colors.BLACK if theme == "light" else ft.Colors.WHITE
return ft.Container(
content=ft.Text(f"Current theme: {theme}", color=text_color),
bgcolor=bg_color,
padding=20,
)
Observable Context Values
Context values can be Observable for automatic updates:
Location: flet/components/hooks/use_context.py:110
from flet.components.observable import Observable
UserContext = ft.create_context(default_value=Observable(None))
@ft.component
def UserProfile():
user = ft.use_context(UserContext)
# Component automatically updates when user.value changes
return ft.Text(f"Logged in as: {user.value['name']}" if user.value else "Not logged in")
Custom Hooks
Create your own hooks by combining built-in ones:
def use_toggle(initial_value: bool = False):
"""Custom hook for boolean toggle state"""
value, set_value = ft.use_state(initial_value)
toggle = ft.use_callback(
lambda: set_value(not value),
dependencies=[value]
)
return value, toggle
@ft.component
def ToggleExample():
is_on, toggle = use_toggle(False)
return ft.Column([
ft.Text("On" if is_on else "Off"),
ft.Switch(value=is_on, on_change=lambda _: toggle())
])
def use_local_storage(key: str, initial_value: any):
"""Custom hook for localStorage persistence"""
stored_value, set_stored_value = ft.use_state(
lambda: load_from_storage(key) or initial_value
)
def set_value(value):
set_stored_value(value)
save_to_storage(key, value)
return stored_value, set_value
Hook Implementation Details
All hooks inherit from the base Hook class:
Location: flet/components/hooks/hook.py:11
Hooks are stored positionally in the component’s _state.hooks list. The use_hook() method manages hook creation and retrieval:
Location: flet/components/component.py:258
Each hook type has its own state container:
StateHook - Stores value, version, and observable subscription
EffectHook - Stores setup, cleanup, dependencies, and async tasks
RefHook - Stores the mutable ref object
MemoHook - Stores memoized value and previous dependencies
ContextHook - Marker for context subscription ordering
Best Practices
- Follow the rules - Never call hooks conditionally or in loops
- Use dependency arrays carefully - Missing dependencies cause stale values
- Prefer functional updates - Use
set_state(lambda prev: prev + 1) for latest values
- Clean up effects - Return cleanup functions to prevent memory leaks
- Extract custom hooks - Reuse stateful logic across components
- Use refs sparingly - Prefer state for values that affect rendering
- Memoize expensive operations - Use
use_memo and use_callback to optimize performance
Next Steps