D
P
0
← All articles Baca dalam Bahasa Indonesia

Next.js & React in Production

Edited `default-menu.ts`, Shipped It, and Staging Still Points at the Old Route: a Sanity Singleton Shadows the Code Default

· · 4 min read
Edited `default-menu.ts`, Shipped It, and Staging Still Points at the Old Route: a Sanity Singleton Shadows the Code Default
const items = doc?.items?.length ? doc.items : DEFAULT_MENU;

That line is the example pattern used by the config getters in the project's data module, and the constant it refers to is the menu default that lives in code. At a glance it reads like a safety net: if the document is not there yet, use the default from code.

What matters more is the direction of the priority. Every Sanity-fetching getter prefers the Sanity document when it exists, and only falls back to the code default when the document is missing or empty. It is not code ruling with the CMS decorating, it is the other way round.

A menu remap that would not show up

This was 1 May 2026, on a Next.js 16 project using Sanity. One part of the main navigation had to change destination, so I edited a single href in lib/default-menu.ts so that it pointed at the new /updates route instead of the old /listing one. Staging kept showing the old mapping.

An expression like the one above raises nothing either. It picks a branch, and that is the end of it.

Reading the line properly

The ternary has two branches, and DEFAULT_MENU lives only in the second one. That second branch is reachable only when doc does not exist at all, or exists with an empty value, because what gets checked is the length of the array. As long as the document is present and its array has items, the expression never glances at the code default.

So my edit was not wrong. It was unreachable.

That shape is not specific to the menu getter either. Every getter that fetches from Sanity uses the same order, document first and default second, so what happened to the menu can happen to any other config that travels the same path.

The document was already there

The investigation ended once the menu config document in Sanity was opened. It still held the structure from the earlier seed, old href included, exactly what staging was rendering.

It had not been written by an editor rearranging the menu in the Studio. It had been written by the project's own seed script, which populates that document through createOrReplace. Once the seed script has populated the document, edits to lib/default-menu.ts or the other code defaults will not appear on the rendered site, because the Sanity document shadows them.

One fact with two homes

Several config documents in that project live in Sanity as singletons, one document per type with a fixed _id.

The menu config holds the top nav and sub nav structure, and its counterpart in code is DEFAULT_MENU in lib/default-menu.ts. The homepage layout decides which sections render and in what order, with DEFAULT_SECTIONS in lib/config-data.ts behind it. The marquee config decides which assets appear in the marquee, with DEFAULT_MARQUEE in the same file. One more holds the provider settings, meaning which providers are enabled along with the last error and success timestamps.

The seed script walks DEFAULT_MENU and the other code defaults, then runs createOrReplace on those singleton documents.

One dataset shared by local and staging

Sanity on this project is shared between local and staging through a single dataset. Re-seeding from any environment therefore propagates everywhere, so there is no separate staging seed to worry about.

The fix is about ordering, not about code

The decision was to add one mandatory step. Whenever lib/default-menu.ts changes, or anything else that surfaces through those singletons, the seed has to run again.

pnpm seed:config

The order of those two steps turns out not to be a free choice, because the two wrong directions carry different prices.

Push code that adds a new route such as /updates without re-seeding, and the old menu document still links to the old hrefs, hiding the new route from users. Conversely, re-seed before the matching code is live, and the menu links to a route that does not exist yet, which gives visitors a 404.

The order written into the project note is re-seed Sanity, then push the code, then wait for the redeploy. The other direction, pushing code first and re-seeding after, costs users roughly 60 seconds of stale nav until the cache expires.

The rules I work by now