PageHeader
PageHeader is used to indicate the title of the current screen and can also provide additional content and actions that relate to the current screen as a whole.
Props
Usage guidelines
- To inform a user about the overall content of a page
- As a header for an overlay surface like a Modal, Popover or Sheet
- As page navigation
- As a title for sections inside of a page—there should only be one page header on a page
- As a toolbar
Best practices
Use only one primary action style in PageHeader. This should also be the only primary action on the page.
Use more than one primary action style in PageHeader, or include a primary action when there’s already a primary action elsewhere on the page. If there's already a primary action elsewhere on the page, PageHeader can have 1 or 2 secondary actions.
Plan for most PageHeaders to be full width. A maxWidth
should only be supplied when the content of the page is center aligned. The PageHeader’s padding should match the page’s overall padding.
Provide maxWidth
for PageHeader content that is different from the page content
Include an image when unique to the page content, such as a page dedicated to a developer’s apps
Include a profile avatar image in PageHeader, as the user avatar should be provided in the main app navigation
Keep additional help buttons and links to a minimum, choosing one source of help per PageHeader
Overload PageHeader with a help IconButton, help Link and info Tooltips. Too many sources of help on the page may confuse users. If there are multiple items to explain, use the help IconButton to open a Sheet with further help. If you want to lead users to external documentation, add a help Link with the helperLink
prop.
Accessibility
Labels
PageHeader has built-in components that require accessibility labels.
- Dropdown (displayed in small screens) requires
dropdownAccessibilityLabel
- IconButton requires
accessibilityLabel
,accessibilityControls
, andaccessibilityExpanded
viahelperIconButton
- Link requires
accessibilityLabel
viahelperLink
Follow the accessibility guidelines for any other Gestalt component passed to primaryaction
, secondaryAction
or items
.
import React from 'react'; import { Button, Datapoint, Dropdown, PageHeader } from 'gestalt'; export default function PageHeaderExample() { return ( <PageHeader title="Ads overview" items={[ <Datapoint size="md" title="Impressions" key="impressions" value="$1.25M" trend={{ value: 30, accessibilityLabel: 'Trending up' }} />, <Datapoint size="md" title="Engagement" key="engagement" value="10%" trend={{ value: 5, accessibilityLabel: 'Trending up' }} />, ]} primaryAction={{ component: <Button color="red" size="lg" text="Promote" />, dropdownItems: [ <Dropdown.Item key="promote" option={{ value: 'Promote', label: 'Promote' }} onSelect={() => {}} />, ], }} secondaryAction={{ component: <Button size="lg" text="View analytics" />, dropdownItems: [ <Dropdown.Link option={{ value: 'View analytics', label: 'View analytics' }} key="view-analytics" href="https://pinterest.com" />, ], }} dropdownAccessibilityLabel="More options" /> ); }
Localization
Be sure to localize the badge
, dropdownAccessibilityLabel
, helperIconButton
, helperLink
, title
, subtext
, as well as any primaryAction
, secondaryAction
and item
components used within PageHeader.
Be brief with text in all components to account for languages with longer words.
import React from 'react'; import { Button, Datapoint, Dropdown, PageHeader } from 'gestalt'; export default function PageHeaderLocalizationExample() { return ( <PageHeader title="Anzeigenübersicht" subtext="5 aktive Kampagnen." helperLink={{ text: 'Mehr erfahren.', accessibilityLabel: 'Erfahren Sie mehr auf Pinterest.com', href: 'http://www.pinterest.com', onClick: () => {}, }} items={[ <Datapoint key="imporessions" size="md" title="Impressionen" value="$1.25M" trend={{ value: 30, accessibilityLabel: 'Aufwärtstrend' }} />, ]} primaryAction={{ component: <Button color="red" size="lg" text="Fördern" />, dropdownItems: [ <Dropdown.Item option={{ value: 'Fördern', label: 'Fördern' }} onSelect={() => {}} key="fordern" />, ], }} secondaryAction={{ component: <Button size="lg" text="Analysen anzeigen" />, dropdownItems: [ <Dropdown.Link key="analysen" option={{ value: 'Analysen anzeigen', label: 'Analysen anzeigen' }} href="https://pinterest.com" />, ], }} dropdownAccessibilityLabel="Mehr Optionen" /> ); }
Variants
Title
PageHeader's title
is the main part of the component as it represents the page's main heading (it will always be a level 1 heading). It can be complemented with three additional elements: a thumbnail (left) and a badge and/or a help Icon (right). Don't forget to localized its content.
import React from 'react'; import { Image, PageHeader, Sheet, Text } from 'gestalt'; export default function PageHeaderTitleExample() { const [open, setOpen] = React.useState(false); return ( <React.Fragment> <PageHeader title="Pinterest app" badge={{ text: 'New', tooltipText: 'New integration' }} helperIconButton={{ accessibilityControls: '', accessibilityExpanded: false, accessibilityLabel: 'Read more information about the new Pinterest integration', onClick: () => setOpen(true), }} thumbnail={ <Image alt="square" fit="cover" naturalHeight={1} naturalWidth={1} src="https://i.ibb.co/LQc8ynn/image.png" /> } /> {open ? ( <Sheet accessibilityDismissButtonLabel="Close" accessibilitySheetLabel="Example sheet for demonstration" heading="Guidance" onDismiss={() => setOpen(false)} size="md" > <Text>1</Text> <Text>2</Text> <Text>3</Text> </Sheet> ) : null} </React.Fragment> ); }
Primary action
PageHeader supports an optional primaryAction
. It can be a Button, a Link or an IconButton with a Tooltip and optional Dropdown. Any Buttons or IconButtons should be size="lg"
.
If there's already a primary action elsewhere on the page, PageHeader can have 1 or 2 secondary actions. Use primaryAction
as an additional secondary action.
Primary and secondary actions are consolidated into Dropdown below the sm breakpoint. primaryAction
takes both the main component and its equivalent using Dropdown subcomponents.
For example, Button should be complemented with Dropdown.Item, Link should be complemented with Dropdown.Link, and an IconButton displaying a Dropdown should reuse the same Dropdown subcomponents. Don't forget to pass dropdownAccessibilityLabel
for the IconButton consolidating all actions into Dropdown below the sm breakpoint.
Resize your window to observe how the PageHeaders below adapt to smaller screen widths.
import React from 'react'; import { Button, Divider, Dropdown, Flex, IconButton, Link, PageHeader, Text, Tooltip, } from 'gestalt'; export default function PrimaryActionExample() { const [open, setOpen] = React.useState(false); const [selected, setSelected] = React.useState([]); const anchorRef = React.useRef(null); const handleSelect = ({ item }) => { if (selected.some((selectedItem) => selectedItem.value === item.value)) { setSelected((localSelected) => localSelected.filter( (selectedItem) => selectedItem.value !== item.value ) ); } else { setSelected((localSelected) => [...localSelected, item]); } }; return ( <Flex direction="column" flex="grow"> <PageHeader title="Product groups" subtext="S. E. All products USD" primaryAction={{ component: <Button color="red" size="lg" text="Create group" />, dropdownItems: [ <Dropdown.Item key="create-group" option={{ value: 'Create group', label: 'Create group' }} onSelect={() => {}} />, ], }} dropdownAccessibilityLabel="Additional options" /> <Divider /> <PageHeader title="Kitchen Reno Ideas" primaryAction={{ component: ( <React.Fragment> <Tooltip idealDirection="up" text="Board options"> <IconButton accessibilityControls="page-header-example" accessibilityHaspopup accessibilityExpanded={open} accessibilityLabel="Board options" icon="ellipsis" iconColor="darkGray" selected={open} onClick={() => setOpen((prevVal) => !prevVal)} ref={anchorRef} size="lg" /> </Tooltip> {open && ( <Dropdown id="page-header-example" anchor={anchorRef.current} onDismiss={() => { setOpen(false); }} > <Dropdown.Item onSelect={handleSelect} selected={selected} option={{ value: 'Edit Board', label: 'Edit Board', }} /> <Dropdown.Item onSelect={handleSelect} selected={selected} option={{ value: 'Share', label: 'Share' }} /> <Dropdown.Item onSelect={handleSelect} selected={selected} badge={{ text: 'New' }} option={{ value: 'Merge', label: 'Merge', }} /> </Dropdown> )} </React.Fragment> ), dropdownItems: [ <Dropdown.Item key="edit-board" option={{ value: 'Edit board', label: 'Edit board' }} onSelect={() => {}} />, <Dropdown.Item key="share-board" option={{ value: 'Share board', label: 'Share board' }} onSelect={() => {}} />, <Dropdown.Item key="merge-board" option={{ value: 'Merge board', label: 'Merge board' }} onSelect={() => {}} />, ], }} dropdownAccessibilityLabel="Additional options" /> <Divider /> <PageHeader title="Ads overview" primaryAction={{ component: ( <Text weight="bold"> <Link href="https://www.pinterest.com"> Switch to quick ad creation </Link> </Text> ), dropdownItems: [ <Dropdown.Link key="Visit" href="https://www.pinterest.com" option={{ value: 'Switch to quick ad creation', label: 'Switch to quick ad creation', }} />, ], }} dropdownAccessibilityLabel="Additional options" /> </Flex> ); }
Secondary action
PageHeader also supports an optional secondaryAction
. It will likely be a Button or an IconButton with a Tooltip and optional Dropdown. Any Buttons or IconButtons should be size="lg"
.
Primary and secondary actions are consolidated into Dropdown below the sm breakpoint. secondaryAction
takes both the main component and its equivalent using Dropdown subcomponents.
For example, Button should be complemented with Dropdown.Item, Link should be complemented with Dropdown.Link, and an IconButton displaying a Dropdown should reused the same Dropdown subcomponents. Don't forget to pass dropdownAccessibilityLabel
for the IconButton consolidating all actions into Dropdown below the sm breakpoint.
Resize your window to observe how the PageHeaders below adapt to smaller screen widths.
import React from 'react'; import { Button, Divider, Dropdown, Flex, IconButton, PageHeader, Tooltip, } from 'gestalt'; export default function SecondaryActionsExample() { const [open, setOpen] = React.useState(false); const [selected, setSelected] = React.useState([]); const anchorRef = React.useRef(null); const handleSelect = ({ item }) => { if (selected.some((selectedItem) => selectedItem.value === item.value)) { setSelected((selectedLocal) => selectedLocal.filter( (selectedItem) => selectedItem.value !== item.value ) ); } else { setSelected((selectedLocal) => [...selectedLocal, item]); } }; return ( <Flex direction="column" flex="grow"> <PageHeader title="Product groups" subtext="S. E. All products USD" primaryAction={{ component: ( <Button color="red" size="lg" text="Create product group" /> ), dropdownItems: [ <Dropdown.Item key="create-product-group" option={{ value: 'Create product group', label: 'Create product group', }} onSelect={() => {}} />, ], }} secondaryAction={{ component: <Button text="Promote" size="lg" />, dropdownItems: [ <Dropdown.Item option={{ value: 'Promote', label: 'Promote' }} key="promote" onSelect={() => {}} />, ], }} dropdownAccessibilityLabel="Additional options" /> <Divider /> <PageHeader title="Custom reports" dropdownAccessibilityLabel="Additional options" primaryAction={{ component: <Button color="red" size="lg" text="Create new report" />, dropdownItems: [ <Dropdown.Item key="create-new-report" option={{ value: 'Create new report', label: 'Create new report', }} onSelect={() => {}} />, ], }} secondaryAction={{ component: ( <React.Fragment> <Tooltip idealDirection="up" text="Board options"> <IconButton accessibilityControls="page-header-example" accessibilityHaspopup accessibilityExpanded={open} accessibilityLabel="Board options" icon="ellipsis" iconColor="darkGray" selected={open} onClick={() => setOpen((prevVal) => !prevVal)} ref={anchorRef} size="lg" /> </Tooltip> {open && ( <Dropdown id="page-header-example" anchor={anchorRef.current} onDismiss={() => { setOpen(false); }} > <Dropdown.Item key="share-report" onSelect={handleSelect} selected={selected} option={{ value: 'Share report', label: 'Share report', }} /> <Dropdown.Item onSelect={handleSelect} selected={selected} option={{ value: 'Help center', label: 'Help center' }} /> <Dropdown.Item onSelect={handleSelect} selected={selected} badge={{ text: 'New' }} option={{ value: 'Delete', label: 'Delete', }} /> </Dropdown> )} </React.Fragment> ), dropdownItems: [ <Dropdown.Item key="share-report" option={{ value: 'Share report', label: 'Share report' }} onSelect={() => {}} />, <Dropdown.Link key="visit-help-center" href="" option={{ value: 'Visit help center', label: 'Visit help center', }} isExternal />, <Dropdown.Item key="delete-report" option={{ value: 'Delete report', label: 'Delete report' }} onSelect={() => {}} />, ], }} /> </Flex> ); }
Complementary items
PageHeader supports an optional pair of components next to the CTA section. It's strongly recommended to limit this space to data display components, mostly Datapoint. The complementary component section is hidden in small breakpoints.
import React from 'react'; import { Button, Datapoint, Dropdown, PageHeader, Sheet, Text } from 'gestalt'; export default function IncludeImageExample() { const [open, setOpen] = React.useState(false); return ( <React.Fragment> <PageHeader title="Ads overview" helperIconButton={{ accessibilityControls: '', accessibilityExpanded: false, accessibilityLabel: 'Read more information about Ads overview', onClick: () => setOpen(true), }} items={[ <Datapoint key="impressions" size="md" title="Impressions" value="$1.25M" trend={{ value: 30, accessibilityLabel: 'Trending up' }} />, <Datapoint key="engagement" size="md" title="Engagement" value="10%" trend={{ value: 5, accessibilityLabel: 'Trending up' }} />, ]} primaryAction={{ component: <Button color="red" size="lg" text="Promote" />, dropdownItems: [ <Dropdown.Item option={{ value: 'Promote', label: 'Promote' }} onSelect={() => {}} key="promote" />, ], }} secondaryAction={{ component: <Button size="lg" text="View analytics" />, dropdownItems: [ <Dropdown.Link key="view-analytics" option={{ value: 'View analytics', label: 'View analytics' }} href="https://pinterest.com" />, ], }} dropdownAccessibilityLabel="More options" /> {open ? ( <Sheet accessibilityDismissButtonLabel="Close" accessibilitySheetLabel="Example sheet for demonstration" heading="Guidance" onDismiss={() => setOpen(false)} size="md" > <Text>1</Text> <Text>2</Text> <Text>3</Text> </Sheet> ) : null} </React.Fragment> ); }
Subtext
subtext
should be used to add metadata about the content on the page, not to describe the page itself. They can be complemented with a helperLink
.
import React from 'react'; import { Button, Dropdown, PageHeader } from 'gestalt'; export default function IncludeImageExample() { return ( <PageHeader title="Create product group" subtext="2,131 catalog products." helperLink={{ accessibilityLabel: '', text: 'Learn more about bulk product catalog uploads.', href: 'http://www.pinterest.com', onClick: () => {}, }} primaryAction={{ component: <Button color="red" size="lg" text="Quick create" />, dropdownItems: [ <Dropdown.Item key="quick-create" option={{ value: 'Quick create', label: 'Quick create' }} onSelect={() => {}} />, ], }} dropdownAccessibilityLabel="Additional options" /> ); }
Max width & border
A maxWidth
should only be supplied when the content of the page is center aligned. The PageHeader’s padding should match the page’s overall padding.
PageHeader also supports a bottom border to show the division between PageHeader and the page content below.
import React from 'react'; import { Box, Dropdown, Flex, IconButton, Heading, PageHeader, SelectList, TextField, Tooltip, } from 'gestalt'; export default function PageHeaderCenterExample() { return ( <Box width="100%" color="secondary" height="100%"> <PageHeader maxWidth="65%" borderStyle="sm" title="Settings" primaryAction={{ component: ( <Tooltip text="Additional options" idealDirection="up"> <IconButton icon="ellipsis" iconColor="darkGray" size="lg" accessibilityLabel="Additional options" /> </Tooltip> ), dropdownItems: [ <Dropdown.Item option={{ value: 'Item', label: 'Item' }} key="item" onSelect={() => {}} />, ], }} dropdownAccessibilityLabel="Additional options" /> <Flex justifyContent="center"> <Box width="60%" paddingY={6}> <Flex direction="column" gap={{ row: 0, column: 5, }} > <Heading size="400" accessibilityLevel={2}> Edit profile </Heading> <TextField label="Name" id="b-textfield1" onChange={() => {}} placeholder="Placeholder" /> <Flex gap={{ row: 2, column: 0, }} > <Flex.Item flex="grow"> <TextField label="Phone" id="b-textfield2" onChange={() => {}} placeholder="Placeholder" /> </Flex.Item> <Flex.Item flex="grow"> <TextField label="Email" id="b-textfield3" onChange={() => {}} placeholder="Placeholder" /> </Flex.Item> </Flex> <SelectList label="Location" id="selectlist1" placeholder="Placeholder" onChange={() => {}} > {[ { value: 'belgium', label: 'Belgium' }, { value: 'france', label: 'France' }, { value: 'usa', label: 'USA' }, ].map(({ label, value }) => ( <SelectList.Option key={label} label={label} value={value} /> ))} </SelectList> </Flex> </Box> </Flex> </Box> ); }
Responsive
PageHeader is responsive to different viewport breakpoints.
Therefore, PageHeader’s behavior relies on the window size and requires PageHeader to be used on a full-window width to correctly respond to different breakpoints. Don’t use PageHeader right next to elements such as side-navigation bars that wouldn’t allow PageHeader to extend the full width of the window.
PageHeader doesn't depend on DeviceTypeProvider to display a mobile view; instead, it adjusts to the smallest viewport breakpoint. The example below forces a mobile viewport width to render Pageheader at that particular viewport.
import React from 'react'; import { Button, Dropdown, IconButton, PageHeader, Tooltip } from 'gestalt'; export default function SecondaryActionsExample() { const [open, setOpen] = React.useState(false); const [selected, setSelected] = React.useState([]); const anchorRef = React.useRef(null); const handleSelect = ({ item }) => { if (selected.some((selectedItem) => selectedItem.value === item.value)) { setSelected((selectedLocal) => selectedLocal.filter( (selectedItem) => selectedItem.value !== item.value ) ); } else { setSelected((selectedLocal) => [...selectedLocal, item]); } }; return ( <PageHeader title="Custom reports" dropdownAccessibilityLabel="Additional options" primaryAction={{ component: <Button color="red" size="lg" text="Create new report" />, dropdownItems: [ <Dropdown.Item key="create-new-report" option={{ value: 'Create new report', label: 'Create new report' }} onSelect={() => {}} />, ], }} secondaryAction={{ component: ( <React.Fragment> <Tooltip idealDirection="up" text="Board options"> <IconButton accessibilityControls="page-header-example" accessibilityHaspopup accessibilityExpanded={open} accessibilityLabel="Board options" icon="ellipsis" iconColor="darkGray" selected={open} onClick={() => setOpen((prevVal) => !prevVal)} ref={anchorRef} size="lg" /> </Tooltip> {open && ( <Dropdown id="page-header-example" anchor={anchorRef.current} onDismiss={() => { setOpen(false); }} > <Dropdown.Item key="share-report" onSelect={handleSelect} selected={selected} option={{ value: 'Share report', label: 'Share report', }} /> <Dropdown.Item onSelect={handleSelect} selected={selected} option={{ value: 'Help center', label: 'Help center' }} /> <Dropdown.Item onSelect={handleSelect} selected={selected} badge={{ text: 'New' }} option={{ value: 'Delete', label: 'Delete', }} /> </Dropdown> )} </React.Fragment> ), dropdownItems: [ <Dropdown.Item key="share-report" option={{ value: 'Share report', label: 'Share report' }} onSelect={() => {}} />, <Dropdown.Link key="visit-help-center" href="" option={{ value: 'Visit help center', label: 'Visit help center' }} isExternal />, <Dropdown.Item key="delete-report" option={{ value: 'Delete report', label: 'Delete report' }} onSelect={() => {}} />, ], }} /> ); }
Writing
- Use sentences for titles capitalizing proper names and product names, including the word “Pin”
- Make sure page titles match the menu item that was used to navigate to the page
- Keep subtext short to account for localization and smaller screens
- Make page titles, subtext and action text lengthy so that it truncates quickly at smaller screen sizes
Component quality checklist
Quality item | Status | Status description |
---|---|---|
Figma Library | Partially ready | Component is live in Figma, however may not be available for all platforms. |
Responsive Web | Ready | Component is available in code for web and mobile web. |
iOS | Component is not currently available in code for iOS. | |
Android | Component is not currently available in code for Android. |
Related
Heading
Heading allows you to show headings on the page, therefore, it should be used to create level 2-6 headings on a page. If a level 1 heading is needed, use PageHeader. Use as a title for sections below PageHeader, or for when a page needs a title but doesn’t warrant a PageHeader.