Upgrade to the developer preview
Developer preview
This page tells you how to move an app from version 20.81.x of @uniformdev/next-app-router to the developer preview, version 20.81.1-alpha.25.sha-f076f9b857. If your app uses version 1 of the SDK (@uniformdev/canvas-next-rsc), first do the steps in Upgrade from v1.
The upgrade has two parts:
- The minimum steps. After these steps, the app builds and runs on the developer preview in lite mode. The browser chooses the personalization and test variants.
- Turn on edge mode. After these steps, the edge chooses the variants of each visitor before the first paint.
For a full example, refer to the changes in the Hello World starter: uniformdev/examples#341.
What changes#
| Area | Version 20.81 | Developer preview |
|---|---|---|
| Middleware | Calls the Route API, evaluates personalizations and tests, and applies redirects and 404s | Makes no network calls in lite mode. It writes the request state into the code. |
| Cached pages | One page for each combination of variants | One page for each route |
| Variants | Chosen in the middleware | Chosen in the browser (lite mode) or at the edge (edge mode) |
| Redirects and 404s | Applied in the middleware | Applied on the page by requireComposition |
| Change the composition data | A custom DataClient | Resolve the composition on the page, and change it there |
| Edge personalization | Not available | vercelUniformEdgeMiddleware or uniformEdgeMiddleware |
warning
Version 20.81 chose the variants in the middleware, so visitors saw no variant change. After Part 1, the server renders the default variants, and the browser changes them after hydration. Visitors who do not get the default variants see the change. Do Part 2 to keep the first paint correct.
Part 1: The minimum steps#
Step 1: Install the packages#
Move all @uniformdev packages to the same developer preview version:
Install every other @uniformdev package of your app with the same version, for example @uniformdev/canvas, @uniformdev/insights and @uniformdev/cli.
A peer dependency range such as ^20.72 does not include developer preview versions. Thus npm can show an ERESOLVE error for a package such as @uniformdev/toolbar-react. Then add an override to package.json, so that the package uses the version of your app:
package.json
Do not install @uniformdev/context-edge. The edge and vercel entry points of @uniformdev/next-app-router contain it.
Step 2: Remove the removed middleware options#
Keep uniformMiddleware or handleUniformRoute for now.
- Remove the
dataClientandpathPatternsWithVariationsoptions. - The middleware no longer reads the query strings of the project map nodes. If your nodes declare query strings, list them in the
queryStringsoption. - Keep the file as
middleware.ts, withruntime: "experimental-edge". Do not rename it toproxy.ts.
Step 3: Update the composition route#
UniformComposition keeps its props, but not dataClient. resolveRoute is now optional, and its default is resolveRouteFromCode.
app/uniform/[code]/page.tsx
createUniformStaticParamsnow makes one code for each path, not one code for each variant combination.- List each
defaultConsentvalue that your middleware can write in thedefaultConsentoption. If the middleware does not setdefaultConsent, leave out the option.
For more information, refer to Static generation (ISR).
If a page renders more than one composition, wrap them in one UniformContext. Render each composition with UniformResolvedComposition:
Step 4: Clean up the server configuration#
- Remove
middlewareRuntimeCache. - Remove
experimental.disableSwrMiddlewareCache. - Add your custom personalization selection algorithms to
context.personalizationSelectionAlgorithms. Refer to Custom personalization algorithms. - Set
defaultConsentin your file. Your file replaces the default configuration, so a missingdefaultConsentisfalse.
Step 5: Replace the removed APIs#
| Removed API | Use this |
|---|---|
DataClient, DefaultDataClient, EnhanceRouteOptions | Resolve the composition on the page, and change it before render. Refer to Change the composition data before render. |
The dataClient option of the middleware, UniformComposition and resolveRouteFromCode | Remove it. |
precomputeComposition | Edge mode (Part 2). The edge chooses the variants of each visitor. |
expireMiddlewareCacheTag | onRevalidateTags of createPreviewPOSTRouteHandler (Part 2). |
pageState.components, pageState.rules, pageState.quirks, pageState.isPrefetch, pageState.requestPath | No replacement. The code no longer contains evaluation results. Read quirks in the browser with useQuirks. |
result.code of ResolvedRouteResult | No replacement. |
getRuleId, resolveComponentFromPageState, resolveRuleFromPageState, PageStateComponent, PageStateComponentFields (from @uniformdev/next-app-router-shared) | No replacement. |
The types GetRouteOptions, GetRouteFromMiddlewareOptions, GetRouteFromPageStateOptions, RewriteRouteOptions, RewriteRouteResult | No replacement. |
ClientContextTestTransfer (from @uniformdev/next-app-router-client) | No replacement. |
Step 6: Build and do a test#
- Run
npm run build. Make sure that the build passes. - Run
npm run start, and open a page that has a personalization. - Make sure that the page shows the default variant first, and then the variant of the visitor.
- Open the page in Canvas preview. Make sure that you can edit it.
The app now uses the developer preview in lite mode.
Part 2: Turn on edge mode#
Do these steps after Part 1. They use the Vercel edge middleware. For other hosts, refer to Set up edge mode on other hosts.
Step 7: Install the Vercel functions package#
Step 8: Replace the middleware#
Replace uniformMiddleware or handleUniformRoute with vercelUniformEdgeMiddleware. Keep your options, and add the missing header rule to the matcher:
middleware.ts
The middleware loads the published Context manifest at runtime, and keeps it in the Vercel runtime cache.
Step 9: Prebuild the two edge mode values#
The default filter of vercelUniformEdgeMiddleware skips pages that it learns have no placements. These pages get a code without the edge mode flag. Thus prebuild the two values:
app/uniform/[code]/page.tsx
Step 10: Expire the edge cache from the preview route#
app/api/preview/route.ts
expireVercelRuntimeCacheTags expires the manifest and the edge route records in the Vercel runtime cache. A webhook expires the records only of static project map paths. Refer to Choose the pages for the edge.
Make sure that a Uniform webhook sends these events to /api/preview: manifest.published, composition.*, entry.*, projectmap.node.* and redirect.*. Refer to Caching.
Step 11: Do a test of edge mode#
Edge mode works only on published pages from the cache. Do the test on a production build:
Build and start the app:
npm run build && npm run startThe build output shows the prebuilt codes of
/uniform/[code], two for each path that has a composition.Look at the headers of a published page:
curl -sI http://localhost:3000/The response has
cache-control: private, no-store, orprivate, no-cachewith anetag.Open a page that has a personalization, with a URL that activates a signal. For example, use
?utm_campaign=launchfor a signal that reads theutm_campaignquery string. Look at the HTML:curl -s "http://localhost:3000/?utm_campaign=launch"The HTML contains only the personalized variant. The
__UNIFORM_DATA__script contains the scores of the visitor.Open the page in a browser. Make sure that the personalized variant shows on the first paint, and that the console shows no hydration errors.
Deploy to Vercel, and publish a change in Uniform. Make sure that the page shows the change.
Behavior changes#
- Redirects: the page applies Uniform redirects. A 301 or 308 redirect gives a 308. All other redirects give a 307. In draft and editor states, the Route API ignores redirects.
- 404s: the page calls
notFound(). The middleware no longer rewrites to/404. Useapp/not-found.tsxfor the 404 page. - Query strings: the middleware removes query strings from the route path, unless you list them in
queryStrings. Thus campaign parameters such asutm_campaigndo not make a new cached page. - Visibility rules: they run only in the browser. A component with visibility rules is not in the server HTML.
- Quirks: the middleware sends quirks to the browser in the
ufqccookie for each request that has quirks. The middleware no longer checks consent or changes before it sets the cookie. - Page codes: the code format changed from
2~…to3~…. Links to/uniform/2~…paths do not work. - Default consent: the middleware always writes the default consent into the code.
- Personalization props:
PersonalizeProps.indexesis nowdefaultIndexes, andTestProps.indexis nowdefaultIndex.PersonalizePropsalso has a newpersonalizationprop. This affects only custom components that replace the SDK placement components. UniformPlayground: it takescodewith an optionalresolveRoute, or aresult. Withcode, a missing pattern gives a 404.- Edge state script:
UniformContextadds the__UNIFORM_DATA__script on pages in edge mode. Do not add it to your layout. - Cached resolvers: the
resolveRouteFromCodeof@uniformdev/next-app-router/cachenow caches only the published state. Draft and editor requests do not use the cache. - Page state:
PageState.defaultConsentis now a requiredboolean. Code that makes aPageStatemust set it. - Slugs: the new
resolveCompositionBySlugfinds the composition ID from the slug, and then gets the composition by its ID. After a publish that changes the slug, the old slug gives no composition.
Checklist#
Part 1: the minimum steps
- [ ] All
@uniformdevpackages use20.81.1-alpha.25.sha-f076f9b857. - [ ]
middleware.tshas nodataClientorpathPatternsWithVariationsoption, and hasruntime: "experimental-edge". - [ ]
UniformCompositionhas nodataClientprop. - [ ]
generateStaticParamslists eachdefaultConsentvalue of the middleware. - [ ] The server configuration has no
middlewareRuntimeCacheorexperimentaloption, and setsdefaultConsent. - [ ] No code uses the removed APIs.
- [ ]
npm run buildpasses, and Canvas preview works.
Part 2: edge mode
- [ ]
@vercel/functionsis installed. - [ ]
middleware.tsusesvercelUniformEdgeMiddleware, and the matcher has themissingrule forx-uniform-edge-origin. - [ ]
generateStaticParamsusesedgeMode: [true, false]. - [ ] The POST preview handler has
onRevalidateTags: expireVercelRuntimeCacheTags. - [ ] A page with a personalization shows the correct variant in the first HTML response.