React Bottom Navigation Bar in Next.js: Safe Areas, Badges
A React bottom navigation bar is the tab bar that native apps have trained everyone to expect: three to five destinations on the bottom edge, where the thumb already is. On the web it takes more than a fixed <div> of icons. The bar has to clear the iPhone home indicator, keep the last row of the page visible, tell a screen reader that Inbox has 4 unread messages, follow the router, and decide what a big center button and a long scroll should do. This guide covers each with the Wingo UI Bottom Tab Bar, then the cases where a Sidebar is the better choice.
When does a web app need a bottom navigation bar?
When people use it on phones and move between three to five top-level sections many times a session. Android's navigation bar guidance (opens in a new tab) lists the conditions: "Three to five destinations of equal importance", "Compact window sizes" and "Consistent destinations across app screens". A CRM with Home, Clients, Inbox and Profile fits. A marketing site, a docs site or a checkout does not, because nobody hops between their sections.
The bottom edge is the reason to bother. In Steven Hoober's 2013 field study (opens in a new tab) (1,333 observations of people using mobile devices), 49% of those touching the screen held the phone in one hand. A hamburger menu in a top corner sits far from that thumb and hides every destination behind an extra tap. In a bottom tab bar web app, every destination stays visible and one tap away, and the active tab tells people where they are.
What does a bottom tab bar need to work in a browser?
Five jobs that a native tab bar gets from the platform and a web page has to do itself:
Build the tabs from links. The ARIA tabs pattern (opens in a new tab) describes "layered sections of content, known as tab panels, that display one panel of content at a time". A tab bar loads another page, so role="tablist" would announce a widget that is not there.
Here is a React mobile bottom navigation for the Next.js App Router, in two files:
The bar is fixed to the viewport and measures itself, safe area included, so the padding on <main> is always exact; hidden by md:hidden on a desktop, it measures 0. A guessed pb-16 is wrong as soon as the inset or the size changes. The safe area guide explains the CSS underneath. Render the bar in the server HTML and hide it with CSS: deciding with a useIsMobile() hook renders nothing on the server, and the bar pops in after hydration, as the useMobile hook guide shows.
$ npx wingo-ui@latest add bottom-tab-barHow should tab bar badges and labels behave?
A count for things that need action, a dot for something new, both read as part of the tab's name. badge: 4 renders a count (99+ above 99) and dot: true an 8px dot, red by default (badgeTone="danger") because people read a red count as unread. The visible badge is aria-hidden, and the link is named "Inbox, 4 unread" (badgeLabel) or "Profile, new" (dotLabel), so a screen reader never reads a stray number. Apple's tab bar guidelines (opens in a new tab) (updated June 2026) say "Reserve badges for critical information so you don't dilute their impact and meaning": unread messages and pending approvals, not a new feature.
Keep showLabels="always". The same guidelines say "Include tab labels to help with navigation" and "Use single words whenever possible". "active" and "never" exist for icon-heavy consumer apps; with "never", labels stay in the DOM for screen readers and become title tooltips. activeIcon swaps in a second glyph while the tab is active, such as a filled version of the outline icon. Apple goes further and asks you to "Prefer filled symbols or icons for consistency with the platform" on every tab.
Should the center button be a tab, an action or a FAB?
An action, never a tab, and only when one creation action dominates the app. Apple is strict here: "Use a tab bar to support navigation, not to provide actions." We still ship the raised center button, because in an app built around one verb (Post, Sell, Scan) that verb has earned the most reachable spot. The action prop renders it in the middle (with four tabs: two, the action, two), filled with the tone and named by its label. It never takes the active pill, so nobody mistakes it for where they are.
When the app has several things to create, or you follow Apple's rule, leave the bar to navigation and use a FAB. It reads --bottom-bar-offset, so it floats 16px above the tab bar and the home indicator without counting the inset twice:
$ npx wingo-ui@latest add fabShould a bottom navigation bar hide on scroll?
Only on long feeds and reading screens, and only if it comes back the moment the user might need it. Apple warns that "If you hide the tab bar, people can forget which area of the app they're in". Its iOS guidance describes minimizing a tab bar that carries an accessory, such as the MiniPlayer in Music, while people scroll down; tapping a tab or scrolling to the top brings it back.
If you hide it, hideOnScroll shows the behavior to copy:
- It follows the finger. The bar moves pixel for pixel with the scroll until it has traveled its own height plus the safe area, and snaps to shown or hidden once scrolling stops. A boolean that flips on direction makes the bar jump on a 2px scroll.
- It comes back on scroll up, at the top, at the end of the content and whenever keyboard focus is inside it.
- It moves a transform only. The progress lives in a motion value, so scrolling never re-renders the bar or the page. A version that calls
setStatein the scroll handler renders on every scroll event. - Reduced motion turns the slide into a flip after a full bar height of scrolling.
$ npx wingo-ui@latest add bottom-tab-barWhen is a sidebar the better choice?
When there are more than five top-level destinations, when they nest, or when most sessions happen on a laptop. Apple suggests "a sidebar or a tab bar that adapts to a sidebar as an alternative for an app with a complex information structure" and asks you to "Avoid overflow tabs"; Android recommends the bar for compact windows.
Most apps need both, so the App Shell renders the Sidebar beside the page from 768px and, below it, the same sidebar in a drawer plus a tab bar for the daily destinations:
nav holds every destination in groups and tabs the four people open every day. The shell renders the tab bar itself and reads the route from a LinkProvider (component={Link}, currentPath={usePathname()}) set once in a client provider; the Next.js admin dashboard tutorial builds that provider and the whole layout. The Admin Shell adds a More tab that opens the drawer.
If you came looking for a shadcn mobile bottom navigation: as of October 2026, the shadcn/ui component list (opens in a new tab) has none, and its Sidebar (opens in a new tab) becomes an 18rem Sheet below 768px, two taps from any destination. Material UI's Bottom Navigation (opens in a new tab) is a fine react bottom navigation component, and the better pick if your app already runs on Material UI. As of October 2026 its docs cover labels, fixed positioning and router links, and leave badges, safe areas and hide on scroll to you.
What breaks a bottom navigation bar in production?
- Home is active on every page. A hand-rolled
pathname.startsWith(href)matches/everywhere. The bar matches/only exactly and picks the longest matchinghref, so/clients/northwind-techkeeps Clients active. - Tapping the active tab does nothing. People expect it to scroll to the top or go back to the tab's first screen.
scrollToTopOnReselectdoes both, andonReselectcan refresh a feed. - A sixth tab. The bar warns in development above five items. Move the rest into a More tab or the drawer.
- Safari's floating toolbar. Since iOS 26, Safari's toolbar floats over the bottom of the page in the Compact tab layout, and a September 2025 forum report (opens in a new tab) says Safari would not render fixed content below those controls. Test on a real iPhone in the Compact, Bottom and Top tab layouts.
- Two bars, two insets. A buy bar above the tab bar must not pad the home indicator again; the mobile-first React components guide shows the stacking.
What should you install?
npx wingo-ui@latest add bottom-tab-bar copies the component into components/ui/ with the Badge, the navigation and motion helpers and the scroll hooks it imports. The Bottom Tab Bar, FAB, Sidebar and App Shell are part of Wingo UI Pro; the Badge is free. More guides like this one live in Mobile UI in React.
FAQ
How many items should a bottom navigation bar have?
Three to five. Android's guidance lists three to five destinations of equal importance, and Apple's tab bar guidelines ask you to avoid overflow tabs. With more sections, keep the four most used in the bar and put everything in a drawer or a sidebar.
Does shadcn/ui have a bottom navigation component?
Not as of October 2026: its component list has no bottom navigation or tab bar, and its Sidebar turns into an 18rem Sheet below 768px. Wingo UI's Bottom Tab Bar, a Pro component, installs the same way, as source code in your project, with npx wingo-ui@latest add bottom-tab-bar.
Should a bottom navigation bar use role="tablist"?
No. The ARIA tabs pattern is for panels that switch inside one page. A bar that changes the page is navigation: a nav element with links, and aria-current="page" on the active one.
Should a bottom navigation bar hide on scroll?
Only on long feeds and reading screens, because a hidden bar also hides where people are in the app. If it hides, it should follow the finger, move with a transform only, and come back on scroll up, at the top and end of the page, and while keyboard focus is inside it.
Is the Wingo UI Bottom Tab Bar free?
No. The Wingo UI Bottom Tab Bar is a Pro component, like the FAB, the Sidebar and the App Shell; the Badge it uses for counts is free.
- Mobile UI
- Navigation
- Tab bar
- React
- Next.js