# List-detail layout

> Shows a list and the selected item side by side, or one after the other on small windows.

- Category: Layout and structure
- API status: Expressive
- Compose APIs: `ListDetailPaneScaffold`, `AnimatedPane`, `rememberListDetailPaneScaffoldNavigator`, `ListDetailPaneScaffoldRole`
- Interactive page: https://m3e.ljzapps.app/#components/list-detail

## When to use

ListDetailPaneScaffold is the canonical layout for master and detail content such as mail or contacts. The window size decides how many panes are visible. On a compact window the detail pane replaces the list, on larger windows both are shown.

The navigator remembers the selected item and the back stack, so back navigation closes the detail pane first. Resize the browser window to watch the layout adapt.

## Examples

### Contacts

Resize the window to switch between one and two panes.

```kotlin
val navigator = rememberListDetailPaneScaffoldNavigator<String>()
val scope = rememberCoroutineScope()
val names = listOf("Maya Chen", "Jonas Weber", "Ana Silva", "Kenji Sato")

Box(Modifier.fillMaxWidth().height(340.dp)) {
    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        scaffoldState = navigator.scaffoldState,
        listPane = {
            AnimatedPane {
                Box(Modifier.fillMaxSize()) {
                    LazyColumn {
                        items(names) { name ->
                            ListItem(
                                onClick = { scope.launch { navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, name) } },
                                supportingContent = { Text("Tap to open") },
                            ) { Text(name) }
                        }
                    }
                }
            }
        },
        detailPane = {
            AnimatedPane {
                Box(Modifier.fillMaxSize().padding(24.dp), contentAlignment = Alignment.Center) {
                    val selected = navigator.currentDestination?.contentKey
                    if (selected == null) {
                        Text("Select a contact", style = MaterialTheme.typography.bodyLarge)
                    } else {
                        Text(selected, style = MaterialTheme.typography.headlineMedium)
                    }
                }
            }
        },
    )
}
```

## Parameters

| Name | Type | Default | Description |
|---|---|---|---|
| `directive` | `PaneScaffoldDirective` | `navigator.scaffoldDirective` | Derived from the window size class and posture. |
| `scaffoldState` | `ThreePaneScaffoldState` | `navigator.scaffoldState` | Which panes are visible. |
| `listPane / detailPane` | `@Composable ThreePaneScaffoldPaneScope.() -> Unit` |  | Wrap content in AnimatedPane. |
| `extraPane` | `@Composable (...)?` | `null` | Optional third pane for large windows. |

## Guidelines

Do:
- Keep the list scroll position when going back.
- Show an empty state in the detail pane.

Don't:
- Do not duplicate the selected item's title in both panes.
- Do not use it for unrelated content.

## Accessibility

- When the detail pane replaces the list, move focus to it and return focus to the selected item on back.
- The back button of the browser should close the detail pane. Wire the navigator into your navigation.

## Related components

- [Supporting pane layout](https://m3e.ljzapps.app/docs/components/supporting-pane.md)
- [Navigation rail](https://m3e.ljzapps.app/docs/components/navigation-rail.md)
- [Drag handle](https://m3e.ljzapps.app/docs/components/drag-handle.md)
