Locator Upgrade - Next.js Steps
Store finder pages on Next.js
Search engines and AI assistants often cannot see your store finder. When they visit the page, your stores are not on it yet: they only appear later, on the visitor's screen. So your stores can be missing from search results and from the answers AI assistants give.
After these steps, every store finder page arrives with its stores already on it, with names, addresses and opening hours. Search engines and AI assistants can find each of your stores and show it. Your visitors see the store finder as usual.
It works for a store finder already on your site and for a new one. It takes five steps, in your site's code. They are written for the developer who maintains the site.
What you need
A site on Next.js 15 or newer, with the App Router, that renders its pages on the server
Access to the site's code, and to the environment variables of the server it runs on
The Uberall Store Finder package for Next.js, which you install in step 2: uberall-store-finder-nextjs-0.1.1.tgz
Your render token, from Uberall
Your locator key, from Uberall. If the store finder is already on your site, it is the data-key of its snippet:
<div id="store-finder-widget" data-key="YOUR_LOCATOR_KEY"></div>
Your store finder's address: the page it is on, such as https://www.example.com/store-locator. Each store gets its own address under it
These steps work for a site whose pages Next.js renders on the server, with the App Router (the app/ folder), on your own Node.js server or on a host that runs Next.js that way.
They do not work for:
- a site exported as static files (
output: 'export'innext.config): no server builds the page when a visitor asks for it, so there is nothing to put the stores into - pages on the Pages Router (the
pages/folder)
There, the store finder keeps working as it does today, on the visitor's screen.
If the store finder is already on your site
After these steps, your store finder pages use only the settings saved at Uberall. What is set on the page itself no longer applies there:
- attributes on the store finder's
div, other thandata-key,data-language,data-countriesanddata-widgetoriginurl - settings your site's scripts pass to
UberallStorefinderMountorUberallStorefinderRestart, other thanwithoutMap
To find them, look in two places:
-
The store finder's
div, in the page's source: anydata-attribute other than the four above. -
Your site's scripts, in your components and in the scripts your layout loads: calls to
UberallStorefinderMountorUberallStorefinderRestart.
If you find any of these settings, send them to Uberall before you start, to be saved with your store finder. Until then, leave the page as it is: the store finder keeps working as it does today.
Your pages' title and description
Uberall gives every store finder page its own canonical link. The title, the description and the preview image for social media come from Uberall only when they are set up there:
- The store finder's main page, and its directory pages (the lists of stores by country, region or city): only what is set up at Uberall. With nothing set up, these pages have no title and no description.
- A store's page: always a title and, when the store has one, a description: the store's name and short description, unless others are set up. The image is the store's photo.
On these pages, your site's own title, description, canonical link and Open Graph tags give way to Uberall's (step 3). So check with Uberall first that a title and a description are set up for your store finder, or send yours to be saved with it.
Step 1: Add your render token to the server's environment
Set the environment variable UBERALL_RENDER_TOKEN to your token where your site runs: in your host's settings for environment variables, or in .env.local while you develop.
UBERALL_RENDER_TOKEN=YOUR_RENDER_TOKEN
Do not start its name with NEXT_PUBLIC_, and do not keep it in your CMS: Next.js sends those to the browser. Read this way, the token stays on your server. Visitors never see it.
Step 2: Install the Uberall Store Finder package
In your site's folder, install the package from Uberall:
npm install https://locator.uberall.com/locator-assets/nextjs/uberall-store-finder-nextjs-0.1.1.tgz
It adds @uberall/store-finder-nextjs to your package.json, at that address. To update it later, install the address of the new version the same way.
The package puts the store finder snippet on the page, with the store finder's content in it, loads the store finder's script after it, and gives your page's head the tags Uberall sends.
Step 3: Put the store finder on its page
Add the store finder's settings as lib/store-finder-settings.ts, and replace YOUR_LOCATOR_KEY and the address with your own:
import type { StoreFinderSettings } from '@uberall/store-finder-nextjs';
export const storeFinderSettings: StoreFinderSettings = {
locatorKey: 'YOUR_LOCATOR_KEY',
widgetOriginUrl: 'https://www.example.com/store-locator',
};
Each store's page lives at an address under the store finder, such as /store-locator/l/berlin/.... So the store finder's page is a catch-all route, which answers all of them. For a store finder at /store-locator, add app/store-locator/[[...path]]/page.tsx:
import { getStoreFinderMetadata, StoreFinder, type StoreFinderPage } from '@uberall/store-finder-nextjs';
import { storeFinderSettings } from '@/lib/store-finder-settings';
export async function generateMetadata(page: StoreFinderPage) {
return getStoreFinderMetadata(storeFinderSettings, page);
}
export default async function StoreLocatorPage(page: StoreFinderPage) {
return <StoreFinder settings={storeFinderSettings} page={page} />;
}
The page hands the package what Next.js gives it, the address's segments and its query, and the package asks Uberall for that address.
If your next.config sets cacheComponents: true, add this line to this page, and to the page in step 4:
export const instant = false;
The store finder's pages wait for Uberall's content before they go out, so they have no part that Next.js could build ahead of the visit. The line tells Next.js so, for these two pages only.
widgetOriginUrl is the store finder's full address, as visitors see it, without a slash at the end. If your page's content comes from a CMS, the settings can come from there too, like the rest of the page. The token cannot (step 1).
generateMetadata takes your site's own title, description, canonical link and Open Graph tags off the page, but only the kinds Uberall sends for it. If your layout writes its own structured data (application/ld+json), leave it off these pages: Uberall sends the store's.
Every address under the store finder's page belongs to the store finder. Do not put other pages under it.
If the page already has the store finder snippet, remove it, once its settings are at Uberall (the section above): the snippet div and its storeFinderWidget-v2.js script. The component puts both on the page. If its addresses have #! in them today, this removes it.
If your snippet has data-language, data-countries or data-widgetoriginurl
Add the same values to the settings:
| Your snippet has | Add to the settings |
|---|---|
data-language="de" |
language: 'de' |
data-countries="US,DE" |
countries: 'US,DE' |
data-widgetoriginurl="https://www.example.com/store-locator" |
widgetOriginUrl: 'https://www.example.com/store-locator' |
Without countries, the page shows the stores of every country set up for the store finder at Uberall.
If your page loads storeFinderWidget-v2-withoutMap.js, or starts the store finder with withoutMap: true, add withoutMap: true to the settings.
Links to the store finder
Links into the store finder can be next/link's <Link> or plain <a>. When the visitor comes to the store finder through a navigation in the browser, such as a <Link> or the Back button, the page loads again in full: React does not run the scripts of a page it builds in the browser, and the store finder starts only on a page that loads in full.
Step 4: Put Uberall's head tags into the page's head
The store finder's styles have to be in the page's <head>, where the store finder looks for them. A page cannot write into <head> itself, so your root layout gets a place for them: a parallel route named @head.
Add app/@head/store-locator/[[...path]]/page.tsx, with the settings from step 3:
import { StoreFinderHead, type StoreFinderPage } from '@uberall/store-finder-nextjs';
import { storeFinderSettings } from '@/lib/store-finder-settings';
export default async function StoreLocatorHead(page: StoreFinderPage) {
return <StoreFinderHead settings={storeFinderSettings} page={page} />;
}
Every other page needs an empty head of its own. Add the same file twice, as app/@head/default.tsx and as app/@head/[...slug]/page.tsx:
export default function NoHead() {
return null;
}
default.tsx covers a page that loads in full, and [...slug] a page the browser goes to from the store finder. Without it, the store finder's head tags stay on that page.
If your site already has a catch-all route at its root, as sites whose pages come from a CMS often do, give this one the same name: next to app/[...path], it is app/@head/[...path]/page.tsx. With two names, Next.js does not build the site.
Then, in your root layout, app/layout.tsx, take head next to children, and put it at the end of <head>:
export default function RootLayout({ children, head }: { children: React.ReactNode; head: React.ReactNode }) {
return (
<html lang="en">
<head>
{/* ... what was already here stays as it is */}
{head}
</head>
<body>{children}</body>
</html>
);
}
Step 5: Deploy and check
Deploy the site with the token from step 1 in its environment.
Open your store finder in the browser, right click, and choose View page source. Search the source for __ubSfSsr. If you find it, it works.
Search the same source for <title and rel="canonical". Each should appear exactly once. If <title is missing, the store finder has no title set up at Uberall yet.
Then open one of your stores and copy its address into a new tab. The store's page should open, with the store's name as the page title.
More than one store finder on the site
Each store finder has its own render token. Give the second one its own environment variable, such as UBERALL_RENDER_TOKEN_2, and pass it in that store finder's settings:
export const otherStoreFinderSettings: StoreFinderSettings = {
locatorKey: 'YOUR_OTHER_LOCATOR_KEY',
widgetOriginUrl: 'https://www.example.com/other-store-locator',
renderToken: process.env.UBERALL_RENDER_TOKEN_2,
};
Then follow steps 3 and 4 for the second store finder's page, with these settings.
If it does not work
| What you see | What to do |
|---|---|
The source has no server render (no token)
|
Add the token, as in step 1, and deploy again. |
The source has no server render (declined: unauthorised)
|
The token is not the one Uberall gave you for this locator key. |
The source has no server render (request failed)
|
Your server cannot reach Uberall. Allow outgoing HTTPS to uberall.com. |
The source has no server render (widgetOriginUrl is not an address)
|
Set widgetOriginUrl to the full address, starting with https:// (step 3). |
The source has another no server render (...)
|
Contact Uberall, with that text. |
The store finder looks unstyled until it starts, or the console shows insertBefore
|
Its head tags are not in <head>: check step 4. |
<title or rel="canonical" appears twice |
Your layout writes its own outside its metadata: leave it off the store finder's pages. |
| A store's page says "Not Found" | The page from step 3 is not a catch-all route: its folder must be named [[...path]]. |
When the store finder content does not arrive
The store finder keeps working in every case. If the content does not arrive, because your server cannot connect to Uberall, the connection takes too long or it returns an error, the page loads without it, and the store finder appears on the visitor's screen as usual.
After such a failure, your server serves the store finder's pages without the content for 30 seconds, and then asks Uberall again.
When changes show
A change at Uberall, to your stores or to your store finder's settings, takes a few minutes to show on these pages.
The store finder's pages are rendered for every visit, so Next.js does not keep them. If your CDN caches your pages, add the time it keeps them: each store finder page in that cache keeps the content it had when it was cached. And a page your cache stored while the content did not arrive stays without its stores, for search engines, until your cache stores it again.