Mobile Navigation
How VitNode's menus behave on phones - a floating bottom bar of up to three links from vitnode.config.ts plus a Menu tab for the rest, and a side drawer behind the user button for the account.
On a phone, the desktop header's navigation has nowhere to go. So below the
sm breakpoint (640px) VitNode splits it in two: your most-used links move to a
floating bottom bar pinned to the bottom of the screen - the kind your thumb
already knows from native apps - and everything else lives in a side drawer
behind the user button at the top.
Both menus come from the navigation option in src/vitnode.config.ts. See
Main Navigation for the item options.
Two menus, one config
| Key | Where visitors see it | Holds |
|---|---|---|
header | In the header on tablets and desktops, and in the side drawer on phones | Any number of items, with dropdowns one level deep |
mobileBottomBar | Pinned to the bottom of the screen, on phones only | Up to three links, no dropdowns, plus a Menu tab |
A page can sit in both menus at once - Search in the header and in the bottom bar is a perfectly normal choice.
import { CompassIcon, MessagesSquareIcon, SearchIcon } from 'lucide-react'
export const vitNodeConfig = buildConfig({
// ...
navigation: {
header: [
// ...
],
mobileBottomBar: [
{ href: '/forum', icon: MessagesSquareIcon, title: 'app.menu.forum' },
{ href: '/discover', icon: CompassIcon, title: 'app.menu.discover' },
{ href: '/search', icon: SearchIcon, title: 'app.menu.search' },
],
},
})A fourth link stops the app at startup with a [VitNode navigation] error, so
the bar never silently drops one.
The bottom bar
Each link is an icon above a short label. The current page is tinted and sits
on a highlight that slides between tabs. A tab counts as current when the URL is
its address or anything below it, so /blog/article/my-first-post keeps a Blog tab lit
(/ only matches the home page itself).
The last tab is always Menu. It opens a drawer from the bottom with the rest of the Header menu - everything the bar does not already show, dropdowns included. On a page that lives in there, the Menu tab is the one lit. If the bar already covers every header link, the Menu tab steps aside.
An empty bottom bar is not drawn at all. That is the default when
navigation.mobileBottomBar is missing, so phones start with just the header and its
side drawer until you add a link.
At 320px each of the four tabs gets about 72px, which fits one word like "Discover" or "Settings" - longer labels are cut off with an ellipsis. A link without an icon shows the first letter of its label instead. The two menus are separate lists, so a bottom bar link can point at a shorter message key than the same page in the header.
The side drawer
The user button stays at the top of the header, where it always was. On a phone it shows the member's avatar (or a person icon for guests), and a tap slides a drawer in from the side with:
- the member's name and email, linking to their profile - or Login and Register buttons for guests,
- the full Header menu, dropdowns included - only while the bottom bar is empty, since otherwise its Menu tab already has it,
- the account links from the desktop user menu (profile, files, settings, AdminCP for admins),
- theme and language switches,
- Log out.
Swipe it away, tap outside it, press Esc or hit the close button. Desktop is untouched: the same button still opens the usual dropdown there.
For developers
- No API involved. The header reads both menus from the config in the browser and on the server, so they render with the page, prerendered pages included.
- Room at the bottom. While the bar is on screen, the page gets extra bottom padding so it never covers your footer.
- Notches and home indicators. The bar sits above the iPhone home indicator
via
env(safe-area-inset-bottom), which needsviewport-fit=coverin your viewport meta tag. New apps have it; in an older app, update the viewport entry insrc/routes/__root.tsx:
{ content: 'width=device-width, initial-scale=1, viewport-fit=cover', name: 'viewport' },- Translations. The drawer's own words - "Menu", "Navigation", "Account" -
live under
core.global.mobile_nav. See Languages & Localization to translate them. Link titles are message keys from your own locale files.