Upgrade notes for partners
This document describes the two changes in this release that can reach an existing app: FlexGrid 3.0.0 takes over the latest dist-tag from FlexGrid 2.x, and the supported toolchain moves to Node.js 24 and pnpm 11.
Neither change is a deprecation. You can stay on FlexGrid 2.x deliberately, and it keeps receiving fixes until approximately October 2026. To avoid upgrading by accident, you must pin the 2.x range. See Staying on your current setup for more information.
The following table lists each change and whether any action is required:
| Change | Action required? |
|---|---|
The @roku-sdk/flex-grid 3.0.0 package now owns latest | Yes, if your range is latest, *, or >=2: pin ^2.0.0, or the install upgrades you across a major version |
The 2.x stable dist-tag changed from latest-2 to maintenance-2x | Yes, if you install by dist-tag |
| The supported runtime is now Node.js 24 and pnpm 11 | Not immediately: installs still succeed on Node 20 with a warning. See the toolchain section for more information about the two cases that fail |
Every @roku-sdk/* package now declares engines.node as >=24.0.0 | No: a dependency's engines field is advisory |
FlexGrid 2.x cell KeyDelegates receive "OK" instead of "ok" | Yes, if a delegate compares key against "ok": use RemoteKey.Select. The type checker does not catch this |
Staying on your current setup
This section describes how to keep an existing app on FlexGrid 2.x and what you get by doing so.
1. Pinning FlexGrid to the 2.x range (required)
The @roku-sdk/flex-grid package publishes two major versions under one package name. Before this release, latest pointed at 2.x; it now points at 3.0.0, which has breaking changes. Any range that can resolve a 3.x version picks one up on your next pnpm update or on a fresh pnpm install without a lockfile.
The following example shows the ranges that resolve to 3.x and the range that stays on 2.x:
// These ranges now resolve to 3.x
"@roku-sdk/flex-grid": "latest"
"@roku-sdk/flex-grid": "*"
"@roku-sdk/flex-grid": ">=2.0.0"
// This range stays on 2.x and keeps receiving 2.x fixes
"@roku-sdk/flex-grid": "^2.0.0"A caret range keeps you on 2.x: ^2.0.0 cannot resolve a 3.x version no matter which major version holds latest; therefore, it stays stable when latest changes owner, now and in the future.
If your lockfile already pins an exact 2.x version, a plain pnpm install does not move you. You must fix the range anyway because the next pnpm update upgrades you.
2. Updating the dist-tag if you install by tag (required)
The SDK renamed the 2.x stable tag so that a bare pnpm add @roku-sdk/flex-grid installs 3.x. The following table lists the old and new tags:
| Channel | Old 2.x tag | New 2.x tag | 3.x tag |
|---|---|---|---|
| stable | latest-2 | maintenance-2x | latest-3, and plain latest |
| rc | rc-2 | rc-2 | rc-3, and plain rc |
| dev | dev-2 | dev-2 | dev-3, and plain dev |
The following example shows the old command and the new command:
# No longer resolves
pnpm add @roku-sdk/flex-grid@latest-2
# Resolves to the 2.x stable release
pnpm add @roku-sdk/flex-grid@maintenance-2xOnly the stable tag changed. The rc-2 and dev-2 tags are unchanged.
3. Node.js and pnpm (recommended, not required)
You can keep building on Node 20 and pnpm 10 for now. See Toolchain for more information about when that stops being true. SDK continuous integration (CI) no longer covers Node 20; therefore, you should treat Node 20 support as best-effort.
What you get by staying on 2.x
Staying on 2.x gives you bug fixes and security patches until approximately October 2026, but no new features, which land on 3.x only. The 2.x line never moves back onto the latest tag.
Moving to FlexGrid 3
This section describes the four steps to move an app from FlexGrid 2.x to 3.x. Peer dependencies are identical between 2.x and 3.x (@roku-sdk/common, components, interfaces, navigation, runtime, solid, all ^2.0.0); therefore, this is a FlexGrid-only upgrade, and no other @roku-sdk/* package has to move with it.
Step 1: Upgrading the toolchain
FlexGrid 3.x declares engines.node >= 24.0.0. You should install Node 24 and pnpm 11 as described under Toolchain.
Step 2: Bumping the range
Change the range in package.json:
"@roku-sdk/flex-grid": "^3.0.0"Then install the new version:
pnpm installThe import specifier does not change; it is still @roku-sdk/flex-grid.
Step 3: Working through the breaking changes
The package's BREAKING_CHANGES.md file lists every change with before and after code, and its src/MIGRATION.md file has the plugin-internals rename table. The following table lists the changes that can affect an app:
| Change | What to do |
|---|---|
createZoomFocusPlugin() removed | Wrap the templates that should scale: withZoomFocus(MyCell, { focusedScale, unfocusedScale }) from @roku-sdk/flex-grid/zoom-focus. Zoom is now set per template when the registry is built; therefore, isZoomEnabled does not have any equivalent |
Select key is "OK", not "ok" | In a cell KeyDelegate, match RemoteKey.Select (re-exported from @roku-sdk/flex-grid). forwardKeyEvent("ok", …) is ignored; use "OK". The SDK removed the select alias; the key now bubbles to the enclosing boundary |
onKeyEvent config option removed | To intercept movement, use a cell KeyDelegate. For region hand-off, use onEdgeReached and setFocus. For back, options, and play, use the onUnhandledKey option of an enclosing FocusBoundary |
| Focus-highlight config keys moved | renderFocusHighlight, focusHighlightVisible, focusHighlightLayer are now options on createFocusHighlightPlugin from @roku-sdk/flex-grid/focus-highlight, passed via plugins |
createPool takes an options object | createPool(cfg, { perf, debugViewport, idPrefix, gridKey }). The removed positional scaleMode and scaleTweenDuration arguments came before perf; therefore, a stale call can still compile while meaning something else. You should check every call site |
Flat GridPluginInternals fields deleted | Custom plugins only: fields moved under rows / focus / scroll / layers. Apps that pass only the built-in plugin factories do not need any change |
computeScrollYGroup renamed to computeScrollY | Custom vertical focus strategies only. The SDK does not provide a fallback; a method that you do not rename silently loses its scroll math. The built-in fixedFocusStrategy and floatingFocusYStrategy already use the new name |
EngineConfig per-row geometry must be an accessor | Direct createFlexGridEngine callers only: rowHeights: () => heights. Going through createFlexGrid / defineFlexGrid still accepts plain arrays |
| The node tree of a pooled cell is one level shallower | Affects only SceneGraph snapshot tests and findNode-style traversal from a cell root. The public API does not change |
onEdgeReached top and bottom now fire at the first and last focusable row | Grids with empty leading or trailing rows now fire the callback where they previously consumed the key press without firing it |
Step 4: Verifying
You should run your unit tests first because a snapshot or key-delegate test catches the "ok" to "OK" change and the shallower pooled node tree immediately. Then check focus visuals and select behavior on a real device.
Rolling back
You can set the range back to ^2.0.0 and reinstall. 2.x remains published and installable for the whole maintenance window.
Toolchain: Node.js 24 and pnpm 11
The following commands install the supported toolchain:
nvm install 24 && nvm use 24
npm install -g [email protected]You must upgrade Node before pnpm, not after. pnpm 11 requires Node 22.13 or later and does not work below it: on Node 20 it aborts with ERR_UNKNOWN_BUILTIN_MODULE: No such built-in module: node:sqlite before running any command. If you are staying on Node 20 for now, you must stay on pnpm 10 as well; the two old versions work together, but pnpm 11 on Node 20 does not work at all.
Does this break my existing app today?
This section describes when the toolchain change affects an existing app. Usually it does not. The engines.node field of a dependency is advisory in both npm and pnpm. The engineStrict setting of pnpm defaults to false; therefore, installing @roku-sdk/* on Node 20 prints a warning and proceeds.
Two cases fail the install:
- Your own
package.jsondeclares"node": ">=24.0.0". pnpm always enforces the project's ownenginesfield, regardless ofengineStrict. Apps that you scaffold withrk createafter this release carry that field; therefore, a new scaffold on Node 20 fails withERR_PNPM_UNSUPPORTED_ENGINE. Apps that you scaffolded earlier declare>=20.0.0and are unaffected until you copy the new value in. - You set
engineStrict: truein your ownpnpm-workspace.yamlor.npmrc. That setting extends enforcement to dependencies, and every@roku-sdk/*package now asks for Node 24.
Beyond installation, SDK CI now tests on Node 24 only; therefore, Node 20 support is best-effort from here. You should plan the upgrade rather than treating the warning as permanent.
pnpm 10 → 11 configuration migration
This section applies only if your own repository carries pnpm configuration. pnpm 11 changed where it reads two settings:
- pnpm 11 no longer reads the
pnpm.overridesandpnpm.auditConfigkeys inpackage.json. You must move them to top-leveloverridesandauditConfigkeys inpnpm-workspace.yaml. Leaving them inpackage.jsondisables them silently, without reporting a warning or an error, and your overrides are not applied. - The
allowBuildssetting replacesonlyBuiltDependenciesandignoredBuiltDependencies. pnpm 11 failspnpm installwhen a dependency has a build script that is neither allowed nor explicitly denied, and becausepnpm runnow performs a dependency-status check first, that failure surfaces on every script you run.
An app that you create with rk create already has the pnpm-workspace.yaml file with these settings. An app that predates 2.15, or one that you copied from the SDK's examples, must have a decision for each of the four packages in an SDK app's tree that have an install script. The following file shows a decision for each package:
# pnpm-workspace.yaml
allowBuilds:
'@roku-sdk/tooling': true # postinstall marks the bundled hermesc executable
'@parcel/watcher': false # native binary ships as an optional platform dependency
esbuild: false # native binary ships as an optional platform dependency
unrs-resolver: false # native binary ships as an optional platform dependency
overrides:
esbuild: '>=0.28.1'The SDK repository's pnpm-workspace.yaml file is an example of both settings.
Troubleshooting
The following table lists common symptoms, their causes, and fixes:
| Symptom | Cause | Fix |
|---|---|---|
No matching version found for @roku-sdk/flex-grid@latest-2 | The 2.x stable tag was renamed | Use @maintenance-2x, or switch to the ^2.0.0 range |
| FlexGrid compiles but focus zoom stops working after an install you did not intend as an upgrade | An unpinned range resolved 3.0.0, where zoom is a template wrapper | Pin ^2.0.0, or complete the 3.x migration |
ERR_PNPM_UNSUPPORTED_ENGINE | Your own package.json requires Node 24 | Upgrade to Node 24, or lower your app's own engines.node |
ERR_UNKNOWN_BUILTIN_MODULE: No such built-in module: node:sqlite | pnpm 11 running on Node 20 | Upgrade Node first: pnpm 11 needs Node 22.13 or later |
EBADENGINE / Unsupported engine warning, install succeeds | A dependency wants Node 24 and you are on Node 20 | Advisory only. Upgrade when convenient |
| pnpm overrides stopped applying after moving to pnpm 11 | pnpm.overrides in package.json is no longer read | Move them to pnpm-workspace.yaml |
pnpm install fails with ERR_PNPM_IGNORED_BUILDS | pnpm 11 requires an explicit allowBuilds decision | Add each listed package to allowBuilds in pnpm-workspace.yaml (see the migration notes) |
Related documentation
- Installing @roku-sdk/* packages from npmjs.com: Describes the registry setup and the full dist-tag policy for packages on their own version line.
- FlexGrid: Describes how to use FlexGrid 3.x.
- FlexGrid API usage: Describes the FlexGrid 3.x API.
Updated about 5 hours ago