Directory layout
Most projects are a single app created by rk create, with SDK packages installed from npmjs.com. You do not need an SDK checkout. If you keep several apps in one repository, a pnpm workspace can share an install while each app still takes its @roku-sdk/* dependencies from the registry.
Single app
Follow Creating an app to scaffold the project. For an app named my-first-app, the main files are:
my-first-app/
├── package.json
├── pnpm-workspace.yaml
├── pnpm-lock.yaml # Written by pnpm install
├── roku-config.ts
├── tsconfig.json
├── eslint.config.mjs
├── jest.config.mjs
├── .prettierrc.json
├── .gitignore
├── src/
│ ├── index.ts
│ ├── components/
│ │ ├── my-first-app.tsx
│ │ └── my-first-app.test.tsx
│ └── compiled-components/
│ ├── BaseAppScene.xml
│ └── BaseAppScene.brs
└── pkg/
├── source/Main.brs
└── images/The scaffold also includes a README and a license. The file table in the first-app guide explains the entry points, scene files and build configuration. tsconfig.json extends the SDK's TypeScript configuration by package alias; the ESLint, Jest and Prettier files select the SDK's development configuration. The consumer package.json shows how registry dependencies are declared without workspace:* ranges.
Put TypeScript code and imported app images in src/. Keep static files that are copied into the channel, including manifest icons and splash images, in pkg/. See Static image management for image imports and resolution variants.
Keep pnpm-workspace.yaml even for one app: it holds the install-script decisions pnpm 11 requires. Commit pnpm-lock.yaml after installing so other developers and CI resolve the same versions.
The CLI creates machine-local state under .roku/ as needed; it is not part of the initial template. A project device-selection override lives at .roku/roku-dev-config.json, while new device registrations normally go to ~/roku-dev-config.json. See Device management for the layered store and password setup. The scaffold's .gitignore contains .roku/, which protects local state and any developer passwords stored there. It does not ignore node_modules/ or build output, so add those entries before committing your app.
Multiple apps in one repository
Use a pnpm workspace when apps belong in the same repository. Each app keeps its own package.json, roku-config.ts, src/ and pkg/. SDK dependencies still use published versions; a workspace containing apps does not require a workspace containing SDK source.
roku-apps/
├── package.json # Private workspace root
├── pnpm-workspace.yaml
├── pnpm-lock.yaml # Shared install lockfile
├── .gitignore
└── apps/
├── app-one/ # Scaffolded app, without pnpm-workspace.yaml
└── app-two/ # Scaffolded app, without pnpm-workspace.yamlCreate a private root package.json with a name and "private": true, and put this configuration in the root pnpm-workspace.yaml before creating the apps:
packages:
- "apps/*"
allowBuilds:
"@roku-sdk/tooling": true
"@parcel/watcher": false
esbuild: false
unrs-resolver: falseThese are the same install-script decisions the standalone scaffold supplies. @roku-sdk/tooling's postinstall sets executable permissions on bundled tools. The other three packages obtain their native binaries through optional platform dependencies, so their install scripts are denied. If you add dependencies with other install scripts, review them and add the appropriate decisions at the root.
From the repository root, scaffold the apps and install:
npx --package @roku-sdk/tooling rk create app-one --output apps --no-install
npx --package @roku-sdk/tooling rk create app-two --output apps --no-install
pnpm installrk create does not write an app-level pnpm-workspace.yaml when the nearest ancestor workspace's packages globs include the new app. It also does not add allowBuilds decisions to that existing root file: you must supply them. If the app is not included by those globs, it gets its own file and is installed as a separate project. Use the block-list packages form shown above so rk create can recognise the workspace.
Run app-specific commands inside the app, where rk can find roku-config.ts:
cd apps/app-one
pnpm exec rk buildUse the same procedure for app-two. Do not add workspace:* ranges for SDK packages: those ranges mean local SDK source packages must exist, not just that your apps share a pnpm workspace.
Device configuration is resolved separately from pnpm workspace membership. rk device add normally writes the machine-wide list at ~/roku-dev-config.json. rk device set-active writes a local selection to the nearest existing .roku/roku-dev-config.json at or above the current directory, or creates one under the current directory when none exists. Device commands and the local device provider use that upward search; they do not automatically select the pnpm root.
For a shared selection, run rk device set-active at the repository root before setting it from either app. Apps without their own override then use the root's .roku/roku-dev-config.json. An app-level file takes precedence over the root's file, so an app can keep a different active device. An existing local file with a legacy devices array can also override the central device list; see Device management before moving an older project. Ignore .roku/ at the root as well as in each app. If you want to run pnpm exec rk at the root, declare @roku-sdk/tooling there with a published version too; otherwise run device commands from an app or use the explicit package bootstrap shown above.
Developing against an unreleased SDK checkout? See Workspace mode.
Updated about 6 hours ago