one
Version:
One is a React framework for web, iOS, and Android apps.
288 lines (235 loc) • 8.4 kB
Markdown
# Stack Header Composition API
A declarative JSX API for configuring native stack headers in One.
## Overview
The Stack Header Composition API provides a more intuitive way to configure stack navigation headers using JSX components instead of the traditional options object. This is particularly useful for complex header configurations like large titles, custom buttons, and search bars.
## Installation
The components are available through the main `one` package:
```tsx
import { Stack } from 'one'
```
## Core Components
| Component | Description |
|-----------|-------------|
| `Stack.Screen` | Screen wrapper with header composition support |
| `Stack.Header` | Main header configuration container |
| `Stack.Header.Title` | Configure title text and large title mode |
| `Stack.Header.Left` | Custom left header content |
| `Stack.Header.Right` | Custom right header content |
| `Stack.Header.BackButton` | Back button configuration |
| `Stack.Header.SearchBar` | Search bar configuration |
## Basic Usage
```tsx
import { Stack } from 'one'
export default function Layout() {
return (
<Stack>
<Stack.Screen name="index">
<Stack.Header>
<Stack.Header.Title large>Welcome</Stack.Header.Title>
</Stack.Header>
</Stack.Screen>
</Stack>
)
}
```
## Large Title with Blur Effect (iOS)
The classic iOS large title pattern with blur effect:
```tsx
<Stack.Screen name="feed">
<Stack.Header blurEffect="regular" style={{ backgroundColor: 'transparent' }}>
<Stack.Header.Title large>Feed</Stack.Header.Title>
</Stack.Header>
</Stack.Screen>
```
## Custom Header Buttons
Add custom buttons to the left or right side of the header:
```tsx
<Stack.Screen name="profile">
<Stack.Header>
<Stack.Header.Title>Profile</Stack.Header.Title>
<Stack.Header.Left asChild>
<Button onPress={goBack}>Back</Button>
</Stack.Header.Left>
<Stack.Header.Right asChild>
<Button onPress={openSettings}>Settings</Button>
</Stack.Header.Right>
</Stack.Header>
</Stack.Screen>
```
## Search Bar
Add an integrated search bar to the header:
```tsx
<Stack.Screen name="search">
<Stack.Header>
<Stack.Header.Title>Search</Stack.Header.Title>
<Stack.Header.SearchBar
placeholder="Search..."
onChangeText={handleSearch}
/>
</Stack.Header>
</Stack.Screen>
```
## Back Button Configuration
Customize the back button appearance and behavior:
```tsx
<Stack.Screen name="detail">
<Stack.Header>
<Stack.Header.Title>Detail</Stack.Header.Title>
<Stack.Header.BackButton
displayMode="minimal"
hidden={false}
>
Back
</Stack.Header.BackButton>
</Stack.Header>
</Stack.Screen>
```
## Custom Header Component
Replace the entire header with a custom component:
```tsx
<Stack.Screen name="custom">
<Stack.Header asChild>
<CustomHeader />
</Stack.Header>
</Stack.Screen>
```
## Combining with Options
You can combine the composition API with the traditional options prop:
```tsx
<Stack.Screen
name="mixed"
options={{
animation: 'slide_from_right',
gestureEnabled: true,
}}
>
<Stack.Header>
<Stack.Header.Title large>Mixed Config</Stack.Header.Title>
</Stack.Header>
</Stack.Screen>
```
## API Reference
### Stack.Header Props
| Prop | Type | Description |
|------|------|-------------|
| `hidden` | `boolean` | Hide the header entirely |
| `asChild` | `boolean` | Render children as the entire header |
| `blurEffect` | `'regular' \| 'prominent' \| ...` | iOS blur effect |
| `style` | `StyleProp` | Header style (backgroundColor, shadowColor) |
| `largeStyle` | `StyleProp` | Large title header style |
### Stack.Header.Title Props
| Prop | Type | Description |
|------|------|-------------|
| `children` | `string` | Title text |
| `large` | `boolean` | Enable large title mode |
| `style` | `StyleProp` | Title text style |
| `largeStyle` | `StyleProp` | Large title text style |
### Stack.Header.Left / Right Props
| Prop | Type | Description |
|------|------|-------------|
| `children` | `ReactNode` | Content to render |
| `asChild` | `boolean` | Required to render custom content |
### Stack.Header.BackButton Props
| Prop | Type | Description |
|------|------|-------------|
| `children` | `string` | Back button title |
| `hidden` | `boolean` | Hide the back button |
| `displayMode` | `'default' \| 'minimal' \| 'generic'` | Display mode |
| `withMenu` | `boolean` | Enable back button menu |
| `src` | `ImageSourcePropType` | Custom back button image |
| `style` | `TextStyle` | Back button title style |
### Stack.Header.SearchBar Props
Accepts all props from `react-native-screens` SearchBarProps.
## Comparison with Options API
**Traditional options API:**
```tsx
<Stack.Screen
name="index"
options={{
title: 'Welcome',
headerLargeTitle: true,
headerBlurEffect: 'regular',
headerRight: () => <Button>Action</Button>,
}}
/>
```
**Composition API:**
```tsx
<Stack.Screen name="index">
<Stack.Header blurEffect="regular">
<Stack.Header.Title large>Welcome</Stack.Header.Title>
<Stack.Header.Right asChild>
<Button>Action</Button>
</Stack.Header.Right>
</Stack.Header>
</Stack.Screen>
```
Both approaches work, choose based on your preference. The composition API can be more readable for complex configurations.
## Stack Toolbar Composition API
Declarative native toolbar items in the same compound style as the header.
One `Stack.Toolbar` declares one `placement`: `left` and `right` compile to
react-navigation 8 alpha iOS header items (`unstable_headerLeftItems` /
`unstable_headerRightItems`); `bottom` (the default) renders the
navigation-controller toolbar in place through the retained `ToolbarHost`
capability, so it must mount in screen content where the responder chain
reaches the screen view controller, and only on iOS.
```tsx
<Stack.Screen name="index">
<Stack.Toolbar placement="left">
<Stack.Toolbar.Button icon="pencil" onPress={edit}>
Edit
</Stack.Toolbar.Button>
</Stack.Toolbar>
<Stack.Toolbar placement="right">
<Stack.Toolbar.Menu title="More" icon="ellipsis.circle">
<Stack.Toolbar.MenuAction icon="square.and.arrow.up" onPress={share}>
Share
</Stack.Toolbar.MenuAction>
</Stack.Toolbar.Menu>
</Stack.Toolbar>
</Stack.Screen>
```
```tsx
// inside screen content, iOS only
<Stack.Toolbar>
<Stack.Toolbar.Button icon="plus" onPress={add}>
Add
</Stack.Toolbar.Button>
<Stack.Toolbar.Spacer />
<Stack.Toolbar.SearchBarSlot />
</Stack.Toolbar>
```
Children are `Button`, `Menu`, `MenuAction`, `Spacer`, `SearchBarSlot`,
`Label`, `Icon`, and `Badge`. A `Button` label is its text children or a
`Label` child; `icon` takes an SF Symbol name or an image source, and an
`Icon` child takes `sf`, `xcasset`, or `src`. `tintColor` passes through as
a platform `ColorValue`, including dynamic iOS values. `variant` selects
`plain`, `done`, or `prominent`. `separateBackground` opts out of the shared
background (iOS 26 Liquid Glass); `hidesSharedBackground` hides it behind
the item. A `Menu` title feeds the menu title and, without a `Label` child,
the bar label; nested `Menu` children become submenus, with `inline` and
`palette` for submenus and `elementSize` for bottom menus. `MenuAction`
takes `isOn`, `destructive`, `subtitle`, and `unstable_keepPresented`.
`Spacer` is flexible, or fixed with `width` (required in left/right).
`SearchBarSlot` is bottom only. `Badge` renders in any placement.
An explicit toolbar always wins over incoming options on its own side: a
toolbar whose items are all hidden writes an empty array and clears
inherited items, while an absent toolbar preserves them. A bottom toolbar in
layout config warns and is ignored. `asChild` renders children as a custom
header element instead of header items (left/right only).
Screen state can drive right items without a new API: render a nameless
`Stack.Screen` with a `Stack.Toolbar` in route content and its options apply
to the current route through `setOptions`.
```tsx
// inside screen content, driven by screen state
const [hidden, setHidden] = useState(false)
return (
<Stack.Screen>
<Stack.Toolbar placement="right">
<Stack.Toolbar.Button icon="magnifyingglass" hidden={hidden} onPress={probe}>
Probe
</Stack.Toolbar.Button>
</Stack.Toolbar>
</Stack.Screen>
)
```