Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
43488d3
feat(appbar): reintroduction of subtitle
Aug 19, 2026
1b82ff5
feat(appbar): flat structure + expressive material design
Aug 21, 2026
025005d
feat(example): example app update
Aug 21, 2026
d2cbdc4
feat(example): handling scrolling behavior
Aug 21, 2026
8a34d7c
fix(appbar): unnecessary animations on actions change
Aug 21, 2026
5a188f7
feat(appbar): filled icon button support
Aug 21, 2026
c2b5f60
feat(appbar): showcase title centering in example app
Aug 24, 2026
9be3287
feat(appbar): title image support
Aug 24, 2026
91c870b
feat(appbar): initial search bar support
Aug 24, 2026
033024d
refactor(appbar): drop titleDisabled
Aug 24, 2026
ca9c2f6
feat(appbar): streamline props
Aug 24, 2026
996b716
refactor(appbar): search width adjustments
Aug 24, 2026
a1b0a4d
feat(appbar): adjust v3 example
Aug 25, 2026
64cd517
refactor(appbar): remove code smells
Aug 25, 2026
83c3c0d
feat(appbar): accessibility related adjustments
Aug 25, 2026
40d3947
test(appbar): accessibility related tests
Aug 25, 2026
1b77102
refactor(appbar): rename props to match M3 conventions
Aug 25, 2026
5b5c4a5
test(appbar): appbar test suite
Aug 25, 2026
ae35538
refactor(appbar): optimizations
Aug 25, 2026
19b043a
feat(appbar): replace v2 with v3 + docs + example app update
Aug 25, 2026
bb814f0
fix(appbar): drop default testID
Aug 26, 2026
b0ce566
fix(appbar): prioritize searchBar style override
Aug 26, 2026
637dc99
fix(appbar): drop search input default centering
Aug 26, 2026
e7948e8
fix(appbar): bring back accessibility related props
Aug 27, 2026
825c8a9
fix(appbar): drop unnecessary a11y prop
Aug 27, 2026
d4ab10d
fix(appbar): drop unnecessary margin from AppbarButton
Aug 27, 2026
d6666cf
fix(appbar): drop low quality optimization from AppbarButton
Aug 27, 2026
825849b
fix(appbar): handling tooltips and menu in buttons
Aug 28, 2026
870db24
docs(appbar): improve docs for appbar
Aug 28, 2026
758a395
docs(appbar): further improvements (screenshots)
Aug 28, 2026
9127f1d
docs(appbar): further clarifications
Aug 28, 2026
bdcb302
docs(appbar): migration to v6.x info
Aug 28, 2026
205af27
docs(appbar): migration example
Aug 28, 2026
2447364
docs(appbar): even more docs improvements
Aug 28, 2026
6f9173d
fix(appbar): satisfy ci's reqs
Sep 1, 2026
a9760ad
Merge branch 'main' into appbar-improvements
Sep 4, 2026
4ede190
fix(appbar): flexible display on web
Sep 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 48 additions & 7 deletions docs/6.x/docs/guides/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ React Native Paper 6 uses [Reanimated](https://docs.swmansion.com/react-native-r

The following props now accept animated styles returned from `useAnimatedStyle`. They no longer accept `Animated.Value` or `Animated.AnimatedInterpolation` where these were previously supported:

- `Appbar.Action` and `Appbar.BackAction`: `style`
- `Badge`: `style`
- `Banner`: `style`
- `Button`: `style`
Expand Down Expand Up @@ -71,20 +70,62 @@ You can use the component's color prop where available, or override the correspo

### Test IDs

Some hardcoded and generated test IDs have been removed for the following components:

- `Appbar.Header`: `${testID}-root-layer`
- `Surface`: `surface` and `${testID}-outer-layer`
The `Surface` test IDs `surface` and `${testID}-outer-layer` have been removed.

You can specify a `testID` explicitly and use that value to query the component.

## Components

### Appbar

The `style` props for `Appbar` and `Appbar.Header` no longer accept `Animated.Value` or `Animated.AnimatedInterpolation`. They only accept static styles.
The Paper 6.x `Appbar` is a major refactor that drops the previous compound component API.

The top-level `style` prop no longer accepts `Animated.Value` or `Animated.AnimatedInterpolation`; it only accepts static styles. The `elevated` prop and `style.elevation` are also removed. Use `isScrolled` to apply the scrolled container treatment.

#### Migrating from the compound API

`Appbar.Header`, `Appbar.Content`, `Appbar.Action`, and `Appbar.BackAction` have been removed. Render one `Appbar` and provide its headline, leading button, and trailing actions as props. The former `mode="medium"` and `mode="large"` values are now `variant="medium-flexible"` and `variant="large-flexible"`; centered content uses `headlineAlignment="center"`.

```tsx
// Before (v5)

const MyComponent = () => (
<Appbar.Header>
<Appbar.BackAction onPress={() => {}} />
<Appbar.Content title="Inbox" />
<Appbar.Action icon="search" onPress={() => {}} />
<Appbar.Action icon="dots-vertical" onPress={() => {}} />
</Appbar.Header>
);

// After (v6)

const MyComponent = () => (
<Appbar
variant="small"
headline="Inbox"
leadingButton={{ type: 'back', onPress: () => {} }}
trailingActions={[
{
key: 'search',
icon: 'magnify',
'aria-label': 'Search',
onPress: () => {},
},
{
key: 'more',
icon: 'dots-vertical',
'aria-label': 'More options',
onPress: () => {},
},
]}
/>
);
```

#### Bottom toolbar support

The `style.elevation` property is no longer supported. Use the `elevated` prop to control Appbar elevation.
Material Design 3 drops the bottom bar support contained previously in the `Appbar` scope and moves it to `Toolbars`, hence you can't use the component to construct a bottom bar anymore - for these cases please use the `Toolbar` component.

### Surface

Expand Down
65 changes: 40 additions & 25 deletions docs/6.x/docs/guides/react-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,7 @@ Now we will implement `CustomNavigationBar` using `AppBar` component:
import { Appbar } from 'react-native-paper';

export default function CustomNavigationBar() {
return (
<Appbar.Header>
<Appbar.Content title="My awesome app" />
</Appbar.Header>
);
return <Appbar variant="small" headline="My awesome app" />;
}
```

Expand All @@ -154,11 +150,7 @@ import { getHeaderTitle } from '@react-navigation/elements';
export default function CustomNavigationBar({ route, options }) {
const title = getHeaderTitle(options, route.name);

return (
<Appbar.Header>
<Appbar.Content title={title} />
</Appbar.Header>
);
return <Appbar variant="small" headline={title} />;
}
```

Expand All @@ -179,10 +171,13 @@ export default function CustomNavigationBar({
const title = getHeaderTitle(options, route.name);

return (
<Appbar.Header>
{back ? <Appbar.BackAction onPress={navigation.goBack} /> : null}
<Appbar.Content title={title} />
</Appbar.Header>
<Appbar
variant="small"
headline={title}
leadingButton={
back ? { type: 'back', onPress: navigation.goBack } : undefined
}
/>
);
}
```
Expand All @@ -194,8 +189,8 @@ export default function CustomNavigationBar({
Another interesting pattern that can be implemented with `react-native-paper` and `react-navigation` is a "menu" button. Thanks to the `Menu` component we can add a nice looking pop-up to our `Appbar`. To implement this feature we need to make a couple of changes in `CustomNavigationBar`:

- Render a `Menu` component
- Pass `Appbar.Action` to the anchor prop
- Add a state to control `Menu` visibility
- Add a trailing action to `Appbar`
- Store the action coordinates and menu visibility in state

:::note
To have properly working `Menu` component, remember to wrap your root component with the `PaperProvider`:
Expand Down Expand Up @@ -236,21 +231,41 @@ export default function CustomNavigationBar({
back,
}) {
const [visible, setVisible] = React.useState(false);
const [menuAnchor, setMenuAnchor] = React.useState({ x: 0, y: 0 });
const openMenu = () => setVisible(true);
const closeMenu = () => setVisible(false);

const title = getHeaderTitle(options, route.name);

return (
<Appbar.Header>
{back ? <Appbar.BackAction onPress={navigation.goBack} /> : null}
<Appbar.Content title={title} />
<>
<Appbar
variant="small"
headline={title}
leadingButton={
back ? { type: 'back', onPress: navigation.goBack } : undefined
}
trailingActions={
back
? []
: [
{
key: 'more',
icon: 'dots-vertical',
'aria-label': 'More options',
onPress: (event) => {
setMenuAnchor({
x: event.nativeEvent.pageX,
y: event.nativeEvent.pageY,
});
openMenu();
},
},
]
}
/>
{!back ? (
<Menu
visible={visible}
onDismiss={closeMenu}
anchor={<Appbar.Action icon="dots-vertical" onPress={openMenu} />}
>
<Menu visible={visible} onDismiss={closeMenu} anchor={menuAnchor}>
<Menu.Item
onPress={() => {
console.log('Option 1 was pressed');
Expand All @@ -272,7 +287,7 @@ export default function CustomNavigationBar({
/>
</Menu>
) : null}
</Appbar.Header>
</>
);
}
```
Expand Down
23 changes: 12 additions & 11 deletions docs/6.x/docs/guides/theming-with-react-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,24 +249,25 @@ Now that the Context is available at every component, all we need to do is impor

```js
import React from 'react';
import { useTheme, Appbar, TouchableRipple, Switch } from 'react-native-paper';
import { Appbar } from 'react-native-paper';
import { PreferencesContext } from './PreferencesContext';

const Header = ({ scene }) => {
const theme = useTheme();
const { toggleTheme, isThemeDark } = React.useContext(PreferencesContext);

return (
<Appbar.Header
theme={{
colors: {
primary: theme?.colors.surface,
<Appbar
variant="small"
headline={scene.route?.name}
trailingActions={[
{
key: 'theme',
icon: isThemeDark ? 'weather-sunny' : 'weather-night',
'aria-label': 'Toggle color theme',
onPress: toggleTheme,
},
}}
>
<Appbar.Content title={scene.route?.name} />
<Switch color={'red'} value={isThemeDark} onValueChange={toggleTheme} />
</Appbar.Header>
]}
/>
);
};
```
Expand Down
6 changes: 3 additions & 3 deletions docs/6.x/docs/guides/theming.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -503,14 +503,14 @@ Now you can use your `FancyButton` component everywhere instead of using `Button
## Dark Theme

Since 3.0 we adapt dark theme to follow [Material design guidelines](https://material.io/design/color/dark-theme.html). <br/>
In contrast to light theme, dark theme by default uses `surface` colour instead of `primary` on large components like `AppBar` or `BottomNavigation`.<br/>
In contrast to light theme, dark theme by default uses `surface` colour instead of `primary` on large components like `BottomNavigation`.<br/>
The dark theme adds a white overlay with opacity depending on elevation of surfaces. It uses it for the better accentuation of surface elevation. Using only shadow is highly imperceptible on dark surfaces.

We are aware that users often use dark theme in their own ways and may not want to use the default dark theme features from the guidelines.<br/>
That's why if you are using dark theme you can switch between two dark theme `mode`s:

- `exact` where everything is like it was before. `Appbar` and `BottomNavigation` will still use primary colour by default.<br/>
- `adaptive` where we follow [Material design guidelines](https://material.io/design/color/dark-theme.html), the surface will use white overlay with opacity to show elevation, `Appbar` and `BottomNavigation` will use surface colour as a background.
- `exact` where everything is like it was before. `BottomNavigation` will still use primary colour by default.<br/>
- `adaptive` where we follow [Material design guidelines](https://material.io/design/color/dark-theme.html), the surface will use white overlay with opacity to show elevation, and `BottomNavigation` will use surface colour as a background.

If you don't use a custom theme, Paper will automatically change between the default theme and the default dark theme, depending on device settings.

Expand Down
9 changes: 4 additions & 5 deletions docs/component-docs.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type Page =
source: string;
component?: string;
props?: string;
propsSource?: string;
title?: string;
};

Expand All @@ -33,11 +34,9 @@ type ComponentDocsConfig = {
const pages = {
ActivityIndicator: 'ActivityIndicator',
Appbar: {
Appbar: 'Appbar/Appbar',
AppbarAction: 'Appbar/AppbarAction',
AppbarBackAction: 'Appbar/AppbarBackAction',
AppbarContent: 'Appbar/AppbarContent',
AppbarHeader: 'Appbar/AppbarHeader',
source: 'Appbar/Appbar',
component: 'Appbar',
propsSource: 'Appbar/types',
},
Avatar: {
AvatarIcon: 'Avatar/AvatarIcon',
Expand Down
47 changes: 35 additions & 12 deletions docs/plugins/component-docs/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,24 +448,47 @@ export const createComponentParser = (tsconfigPath: string) => {
);
const checker = program.getTypeChecker();

return (sourceRootDir: string, page: ComponentPageConfig): ComponentDoc => {
const sourcePath = path.join(sourceRootDir, `${page.source}.tsx`);
const sourceFile = program.getSourceFile(sourcePath);

if (!sourceFile || !fs.existsSync(sourcePath)) {
return fail(
sourcePath,
'source file was not found in the TypeScript program'
);
const getSourceFile = (
sourceRootDir: string,
source: string,
extensions: readonly string[]
) => {
const sourceBasePath = path.join(sourceRootDir, source);

for (const extension of extensions) {
const sourcePath = `${sourceBasePath}${extension}`;
const sourceFile = program.getSourceFile(sourcePath);

if (sourceFile && fs.existsSync(sourcePath)) {
return { sourceFile, sourcePath };
}
}

return fail(
sourceBasePath,
'source file was not found in the TypeScript program'
);
};

return (sourceRootDir: string, page: ComponentPageConfig): ComponentDoc => {
const { sourceFile, sourcePath } = getSourceFile(
sourceRootDir,
page.source,
['.tsx']
);
const propsSource = page.propsSource
? getSourceFile(sourceRootDir, page.propsSource, ['.ts', '.tsx'])
: { sourceFile, sourcePath };

const componentName = page.component ?? getDefaultExportName(sourceFile);
const component = findComponentDeclaration(sourceFile, componentName);
const propsType = findPropsType(
sourceFile,
propsSource.sourceFile,
page.props ?? DEFAULT_PROPS_TYPE
);
validatePropsType(sourceFile, propsType);
if (!page.propsSource) {
validatePropsType(sourceFile, propsType);
}
const defaults = getParameterDefaults(
checker,
sourceFile,
Expand All @@ -474,7 +497,7 @@ export const createComponentParser = (tsconfigPath: string) => {
const props = getProps(
checker,
sourceRootDir,
sourceFile,
propsSource.sourceFile,
propsType,
defaults
);
Expand Down
1 change: 1 addition & 0 deletions docs/plugins/component-docs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export type ComponentPageConfig = {
source: string;
component?: string;
props?: string;
propsSource?: string;
title?: string;
};

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/screenshots/appbar-v6-search.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/screenshots/appbar-v6-small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 6 additions & 10 deletions docs/src/data/screenshots.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
export const screenshots = {
ActivityIndicator: 'screenshots/activity-indicator.gif',
Appbar: 'screenshots/appbar.png',
'Appbar.Action': 'screenshots/appbar-action-android.png',
'Appbar.BackAction': 'screenshots/appbar-backaction-android.png',
'Appbar.Content': 'screenshots/appbar-content.png',
'Appbar.Header': {
small: 'screenshots/appbar-small.png',
medium: 'screenshots/appbar-medium.png',
large: 'screenshots/appbar-large.png',
'center-aligned': 'screenshots/appbar-center-aligned.png',
},
'Avatar.Icon': 'screenshots/avatar-icon.png',
'Avatar.Image': 'screenshots/avatar-image.png',
'Avatar.Text': 'screenshots/avatar-text.png',
Appbar: {
search: 'screenshots/appbar-v6-search.png',
small: 'screenshots/appbar-v6-small.png',
'medium flexible': 'screenshots/appbar-v6-medium-flexible.png',
'large flexible': 'screenshots/appbar-v6-large-flexible.png',
},
Badge: {
'with text': 'screenshots/badge-1.png',
'without text': 'screenshots/badge-2.png',
Expand Down
Loading