Storefront on a phone

Storefront Mobile Menu Not Working: The Menu Button, navigation.js and the Handheld Menu

Below 768 pixels, Storefront hides the header menu, the header search and the header cart. In their place it hands the phone two things: a button labeled Menu and a bar pinned to the bottom of the screen. Both run on small scripts that a single speed setting can silence.

October 16, 2026 · 10 min read

The short answer

When the Storefront mobile menu fails, check four things in order: a speed plugin delaying or combining navigation.js, which waits for the DOMContentLoaded event; an empty Handheld Menu location; a child theme or plugin that replaced storefront_primary_navigation; and a cache serving old markup. Test each one logged out, on a real phone, before changing the next.

On this page
  1. Dead button, page list, missing arrows: the evidence on the phone
  2. How Storefront builds the phone menu, file by file
  3. Five suspects, ranked for a Storefront store
  4. The bottom bar runs on its own script and its own rules
  5. Restoring the Menu button on a live store
  6. A phone check that outlives the next speed plugin
  7. When a dead Menu button is really the stack talking

Dead button, page list, missing arrows: the evidence on the phone

Storefront's phone menu fails in a handful of specific ways, and each one points at a different layer. Before touching a setting, pick up an actual phone, open the store in a private tab, and write down which of these you see and in which browser. A fault that appears only for logged-out visitors is already half diagnosed.

  • Menu does nothing when tapped. The button is visible, which proves the HTML and CSS arrived. The script did not run, or ran too late to listen.
  • First tap dead, second tap works. The usual signature of a speed plugin that holds scripts back until the visitor's first interaction.
  • The menu opens to every page on the site, with Home on top. No menu is assigned to the Handheld Menu location, so WordPress fell back to its page list.
  • The menu opens but parent items have no arrows. The dropdown toggles are built by script from a small inline variable that went missing.
  • Search in the bottom bar reloads the page. The bar's own script never ran, so the link behaves like a plain link to the current address.
  • The bottom bar vanished on cart and checkout. That one is deliberate, and the section on the bar explains why.

How Storefront builds the phone menu, file by file

Storefront's header is a queue of functions on the storefront_header action. At priority 50 sits storefront_primary_navigation, which prints a nav element with the id site-navigation. Inside it come a button with the id site-navigation-menu-toggle, its label passed through the storefront_menu_toggle_text filter, and then two menus: the Primary Menu in a primary-navigation container and the Handheld Menu in a handheld-navigation container. At 768 pixels and wider the stylesheet hides the button and the handheld menu. Below that width the button appears and the handheld menu waits behind it.

The opening is done by assets/js/navigation.min.js, enqueued as storefront-navigation in the footer with no dependencies, so jQuery plays no part. The script registers a listener for DOMContentLoaded. When that fires, it finds site-navigation, its button and the first list inside, hides the button if there is no list, and toggles a toggled class on the nav with each tap. For the handheld menu it then adds a dropdown-toggle button beside every parent item, with screen reader text taken from a variable named storefrontScreenReaderText. WordPress prints that variable inline, and only when the Handheld Menu location has a menu assigned.

Five suspects, ranked for a Storefront store

1. A speed plugin rearranged navigation.js

Delay, defer and combine features change when and in what order scripts run. Delay-until-interaction is the most frequent culprit: the first tap merely wakes the script loader, so Menu seems dead once and then works. View the page source logged out and find the script tag with the id storefront-navigation-js. If its type or address has been rewritten, or it has disappeared into a combined bundle, an optimizer owns it. Exclude navigation.min.js and footer.min.js from delay and combine, purge, and tap again on the phone.

2. The inline variable went missing

If the menu opens but parent items have no arrows, open the browser console at phone width. An error saying storefrontScreenReaderText is not defined means the script ran before its inline variable printed, usually because a combiner moved one and not the other. The main toggle is attached before that line, which is why the menu still opens. Everything after it stops, including the focus handling and the touch handling for submenus. Our guide to theme JavaScript errors shows how to read the console without guessing.

3. Nothing is assigned to Handheld Menu

Storefront registers three locations: Primary Menu, Secondary Menu and Handheld Menu. The handheld menu is printed with no fallback setting of its own, so an empty location falls back to the WordPress page list, and Storefront adds a Home link to that list. Open Appearance, Menus, Manage Locations, or Menu Locations in the Customizer, and assign a short menu to Handheld Menu. Reusing the desktop menu works, but twelve nested items on a phone is a menu nobody finishes reading.

4. A child theme or plugin replaced the navigation

Storefront wraps its template functions in function_exists checks, so a child theme can define its own storefront_primary_navigation. Copies pasted from an old tutorial may drop the handheld menu, rename the button or change the nav id, and navigation.js quietly exits when it cannot find site-navigation or a button inside it. Mega menu plugins that rewrite the primary menu markup can do the same. Search the live source for site-navigation-menu-toggle and handheld-navigation. If the header is thin beyond the menu, Storefront header not showing covers the hook side.

5. A cache is serving an old header

Assigning a menu changes the HTML, and a page cache, a host cache or a CDN keeps serving the previous HTML until someone purges it. Logged-in owners usually bypass those caches, which is how the menu works for you and fails for shoppers. Clear the plugin cache, then the host's, then the CDN's, and check again in a private tab. If styles are stuck too, the same layers are walked through in Storefront CSS not updating.

The bottom bar runs on its own script and its own rules

The handheld footer bar is printed by storefront_handheld_footer_bar on the storefront_footer action at priority 999, and only while WooCommerce is active. It carries My Account, Search and Cart links, drops the account or cart link when that WooCommerce page does not exist, and accepts changes through the storefront_handheld_footer_bar_links filter. It is fixed to the bottom edge with a z-index of 9999, so chat bubbles and cookie banners can collide with it. Below 768 pixels it is the only cart link a shopper has, because the header cart and header search are hidden at that width.

  • Search reloads the page. The Search item is a link with an empty address. footer.js, enqueued as storefront-handheld-footer-bar, cancels that click and opens the search box instead. Delay footer.js and the tap reloads the page.
  • The bar hides while someone types. footer.js adds an sf-input-focused class to the body whenever a form field has focus, which hides the bar. That is intended, not a fault.
  • The bar is gone on cart and checkout. The function returns early once WooCommerce's Cart or Checkout block scripts have loaded. Storefront has disabled the bar on block cart and checkout pages since version 2.5.6.

Removing the bar takes a remove_action on storefront_footer at priority 999, run on init from a child theme or customization plugin. Do it only after a cart link exists somewhere a phone can reach, or small screens lose the basket. If the cart page itself misbehaves, WooCommerce checkout not working is the next page to read.

Restoring the Menu button on a live store

  1. 1

    Reproduce it on a real phone

    Private tab, logged out, on mobile data. Match what you see to the symptom list above and note the browser and screen width.

  2. 2

    Check the markup

    View the page source and search for site-navigation-menu-toggle, handheld-navigation and storefront-handheld-footer-bar. Missing markup means a PHP change; present markup means a script or cache problem.

  3. 3

    Assign the Handheld Menu

    Appearance, Menus, Manage Locations. Give Handheld Menu a short menu built for thumbs, then purge every cache.

  4. 4

    Read the console

    Use Safari's Web Inspector for an iPhone or Chrome's remote devices for Android. Note the first error and the file it names.

  5. 5

    Exempt Storefront's scripts

    Exclude navigation.min.js, footer.min.js and the inline storefrontScreenReaderText variable from delay, defer and combine. If the plugin has no exclusion list, switch those features off one at a time.

  6. 6

    Test the parent theme on staging

    Activate plain Storefront on a copy. If the menu works, compare the child theme's functions with inc/storefront-template-functions.php from 4.6.2.

  7. 7

    Purge and tap everything

    Clear plugin, host and CDN caches, then tap Menu, a parent arrow, Search in the bottom bar and the cart on the phone again.

A phone check that outlives the next speed plugin

Storefront itself changes rarely. Its repository limits work to critical, broad-impact and compatibility fixes, and 4.6.2 arrived in December 2025, nearly eleven months after 4.6.1. So a phone menu that worked last month and fails today was almost certainly broken by something around the theme: a new optimizer setting, a mega menu plugin, a child theme edit, a fresh cache rule. The Storefront review explains what that caretaker status means for the rest of the store.

  • Keep a written list of the scripts excluded in your speed plugin, starting with navigation.min.js and footer.min.js, so nobody quietly undoes the fix.
  • After every plugin, theme or cache change, tap Menu, one parent arrow, Search in the bottom bar and the cart on a real phone.
  • Keep child theme changes to hooks and filters rather than copying whole Storefront functions you then have to maintain by hand.
  • Give the Handheld Menu its own short menu, edited deliberately, instead of borrowing the desktop one by default.

Those checks are routine under our hosting and maintenance plans, done on a phone. If the menu works but responds slowly, the Storefront speed guide and our notes on mobile speed for WordPress themes cover what competes for the phone's attention.

When a dead Menu button is really the stack talking

Storefront's phone menu is about two hundred lines of plain JavaScript and a few CSS rules, and on a clean install it works. It breaks in transit: rewritten by an optimizer, replaced by a plugin, frozen by three caches. That is the WordPress pattern in miniature, and the cross-theme version is laid out in WordPress mobile menu not working. Other Storefront faults, from a broken shop layout onward, sit in the troubleshooting section.

A Next.js store treats the menu as a component in one tested codebase, and nothing rewrites its scripts on the live server. It costs more to build and needs a developer for structural changes, so it earns its keep when phone traffic carries real revenue. Storefront vs Next.js weighs that trade honestly, and Storefront to Next.js maps the move with every product URL kept or redirected. For a one-off repair, our theme troubleshooting team fixes it on staging, and WooCommerce website repair takes over when the store has deeper trouble.

Questions people ask before they call

Because the Handheld Menu location is empty. Storefront prints the phone menu from that location, and when nothing is assigned WordPress falls back to its default list of published pages, with a Home link Storefront adds. Go to Appearance, Menus, Manage Locations, assign a menu to Handheld Menu, then purge your page cache so visitors receive the new markup.

Storefront passes the label through a filter named storefront_menu_toggle_text. Add a small function in a child theme or a customization plugin that returns your own word, such as Shop or Browse. There is no Customizer setting for it. Keep the label short, because the button shares a row with the logo on narrow phones.

Not with a setting. The 768 pixel switch is written into Storefront's compiled stylesheet, in a media query that hides the Menu button and the Handheld Menu on wider screens. Moving it means overriding several rules in a child stylesheet, and navigation.js separately checks for widths above 767 pixels when handling touch tablets. A shorter desktop menu is usually the better fix.

That is intended. On touch screens 768 pixels or wider, Storefront keeps the desktop menu, and navigation.js blocks the first tap on a parent item so its dropdown can open; the second tap follows the link. If even the first tap does nothing, the script is not running, and the speed plugin checks on this page apply.

By design. Below 768 pixels Storefront hides the header cart and the header search, and the handheld footer bar at the bottom of the screen carries Cart, Search and My Account instead. If someone removed that bar, phones lost their cart link. Restore it, or add a Cart item to the Handheld Menu so shoppers can still reach the basket.

Keep reading

Free · No obligation

Get the Menu button working on every phone

Tell us what happens when a customer taps Menu. We reproduce it on real devices, find the layer that broke it and fix it on a staging copy before it touches your live store.

Which of these sounds like you? (pick any)

Rather talk now? Call (210) 346-0848.

CallGet a quote