Migrating from 3.13.x to 4.0.0
Five small breaking changes — all aliased and dev-warned in 3.13.x. If your 3.13.x build runs without warnings in the dev console, your 4.0 upgrade is a one-line bump.
TL;DR
4.0 is the consolidated breaking-change window. Every removed alias or behavioural change was deprecated and dev-warned in 3.13.x, so the upgrade path is:
- On 3.13.x, run your dev server. Any deprecated prop or behaviour fires a one-shot
[ndpr-toolkit/<module>]warning in the console. - Fix each warning at its callsite.
- Bump to
^4.0.0.
pnpm up @tantainnovative/ndpr-toolkit@^4.0.0
# or
bun add @tantainnovative/ndpr-toolkit@^4.0.0
# or
npm install @tantainnovative/ndpr-toolkit@^4.0.0The breaking changes
1. React 17 dropped from peerDependencies
The peer range is now ^18.0.0 || ^19.0.0. The toolkit uses React.useId (18+) in several components, so the previous ^16.8 || ^17 || ^18 || ^19 claim was already a lie — React 17 consumers installed fine then hit cryptic errors. This release just makes the peer range honest.
Action: if you're on React 17 (or earlier), upgrade your app to React 18+ before bumping. If you're on React 18 or 19, no action required.
2. formDescription → description
BreachReportForm and the NDPRBreachReport preset have a description prop matching every other component. The legacy formDescription alias is removed.
// Before (3.x)
<BreachReportForm formDescription="Custom blurb" />
<NDPRBreachReport formDescription="Custom blurb" />
// After (4.0)
<BreachReportForm description="Custom blurb" />
<NDPRBreachReport description="Custom blurb" />3. initialActivities / initialTransfers → initialData
NDPRLawfulBasis and NDPRCrossBorder presets standardised on initialData (matches NDPRROPA). The module-specific aliases are removed.
// Before (3.x)
<NDPRLawfulBasis initialActivities={activities} />
<NDPRCrossBorder initialTransfers={transfers} />
// After (4.0)
<NDPRLawfulBasis initialData={activities} />
<NDPRCrossBorder initialData={transfers} />4. extraOptions removed from NDPRConsent
The one-off extraOptions pattern (which extended the toolkit's defaults) is gone. Pass the full options array via options instead.
// Before (3.x)
<NDPRConsent extraOptions={[{ id: 'research', label: 'Research', ... }]} />
// After (4.0)
<NDPRConsent
options={[
{ id: 'essential', label: 'Essential', description: 'Required', required: true, purpose: 'Site operation' },
{ id: 'analytics', label: 'Analytics', description: 'Usage measurement', required: false, purpose: 'Product analytics' },
{ id: 'research', label: 'Research', description: 'For research', required: false, purpose: 'Research' },
]}
/>5. useDSR default storage flipped to in-memory
useDSR is the admin tracker hook — its state holds data subjects' PII. The previous default stored that PII in the admin's browser localStorage, which is rarely appropriate. 4.0 flips the default to in-memory; localStorage requires explicit opt-in.
// Before (3.x): default useLocalStorage was true
const { requests } = useDSR({ requestTypes });
// → requests persisted to localStorage["ndpr_dsr_requests"]
// After (4.0): default useLocalStorage is false
const { requests } = useDSR({ requestTypes });
// → requests live in component state only; nothing persists
// To keep the old behaviour (rarely the right move):
const { requests } = useDSR({ requestTypes, useLocalStorage: true });
// Recommended: pass an explicit adapter for production
const { requests } = useDSR({
requestTypes,
adapter: apiAdapter('/api/dsr'),
});6. Example rename: dsr-backend-prod → dsr-backend-reference
The example was renamed to be honest about its scope. The "prod" name oversold what it does — it covers DSR receipt and confirmation only, not the full fulfilment pipeline (identity verification, audit logging, status transitions, request-type-specific handling, deadline tracking, DPO routing). The example's README now ships an explicit "What you still need to build" checklist.
Action: if you bookmarked or cloned examples/dsr-backend-prod/, update to examples/dsr-backend-reference/.
Everything else stays the same
- All 19 component
*Propsinterfaces stay exported from the root. - Adapter contract (
StorageAdapter<T>,localStorageAdapter,cookieAdapter,apiAdapter,memoryAdapter,composeAdapters) — unchanged. - The
/server,/headless,/hooks,/presets, and/unstyledsubpaths — unchanged. - Compliance score engine, NDPA validators, all 5 locale files, the
NDPRThemeProvider— unchanged. - i18n wiring across all 20 components (shipped in 3.13.0) — stays in place.
After the bump — verify checklist
tsc --noEmit— no new type errors. If you hit any, they're almost certainly one of the 5 renames above.- Run the app in dev. No more
[ndpr-toolkit/...]deprecation warnings. - If you use
useDSR()without an explicit adapter, the tracker now starts empty on every reload — that's intentional. PassapiAdapteroruseLocalStorage: trueif you need persistence.