Add Package
Overview
Use this skill to scaffold and standardize packages so they look and behave like the existing @remix-run/* packages. Follow this exactly when creating package files, public exports, tests, and docs.
Workflow
- Create the package directory and baseline files.
- Create
packages/<package-name>/.
- Add:
- package.json - tsconfig.json - tsconfig.build.json - CHANGELOG.md - README.md - LICENSE - src/
- For new packages, start
CHANGELOG.md with ## Unreleased as the first section to indicate changes are not released yet.
- Set up
package.json using monorepo conventions.
- name: @remix-run/<package-name> - version (for brand-new packages): "0.0.0" - type: "module" - license: "MIT" - repository.directory: packages/<package-name> - homepage: https://github.com/remix-run/remix/tree/main/packages/<package-name>#readme
- LICENSE - README.md - dist - src - !src/**/*.test.ts
- build: tsgo -p tsconfig.build.json - clean: git clean -fdX - prepublishOnly: pnpm run build - test: remix test - test:bun: bun x --bun remix test - typecheck: tsgo --noEmit
- Use baseline dev dependencies:
- "@remix-run/assert": "workspace:^" - "@remix-run/test": "workspace:^" - "@types/node": "catalog:" - "@typescript/native-preview": "catalog:"
- Add
keywords like existing packages (short, lowercase, feature-focused).
- Define exports with
src entry files only.
- In
exports, map each public subpath to a dedicated file in src.
- Always include
./package.json.
- Mirror each export in
publishConfig.exports with dist output:
- types: ./dist/<entry>.d.ts - default: ./dist/<entry>.js
- Rule: every export must have a
src file that re-exports from src/lib.
- Example: export ./foo -> src/foo.ts -> export { ... } from './lib/foo.ts'
- Type-only exports may map only a
types condition to src/.d.ts and dist/.d.ts.
- Runtime-specific exports may use package conditions before
default when the runtime owns that condition, such as node-hmr for remix/node-hmr/runtime.
- If a package has type-only exports, add
node ../../scripts/copy-package-type-only-exports.ts . to the package build script after tsgo.
- Add TypeScript config files with shared defaults.
Use this tsconfig.json pattern:
{
"compilerOptions": {
"strict": true,
"lib": ["ES2024", "DOM", "DOM.Iterable"],
"module": "ES2022",
"moduleResolution": "Bundler",
"target": "ESNext",
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true,
"verbatimModuleSyntax": true
}
}
Use this tsconfig.build.json pattern:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"outDir": "./dist"
},
"include": ["src"],
"exclude": ["src/**/*.test.ts"]
}
- Implement source structure and test setup.
- src/<entry>.ts for public entry points - src/lib/.ts for implementation - src/lib/.test.ts for tests (colocated with implementation)
- Tests use Remix's test runner:
- import * as assert from '@remix-run/assert' - import { describe, it } from '@remix-run/test'
- Do not generate tests with loops/conditionals inside describe().
- Follow monorepo code style rules while implementing.
- Use
import type { ... } and export type { ... } for types.
- Include
.ts extensions in relative imports.
- Prefer
let for locals; use const only at module scope.
- Never use
var.
- Prefer function declarations/expressions for normal functions.
- Use arrow functions for callbacks; use concise callbacks when returning a single expression.
- Use object method shorthand (
method() {}) instead of arrow properties.
- Use native class fields and
#private members.
- Avoid Node-specific APIs when Web APIs are available.
- Write README in the same style and section order as existing packages.
- # <package-name> - One short paragraph describing purpose.
- ## Features - ## Installation - ## Usage - Optional deep-dive sections (only if needed) - ## Related Packages (if applicable) - ## License
- Installation instructions must always include installing the
remix package.
- If using the package requires a peer dependency, installation instructions must also include that peer dependency in the command.
- Preferred installation pattern:
npm i remix
- Example when a peer dependency is required:
npm i remix <peer-dependency>
- Usage examples must always import from
remix package exports, not from @remix-run/<package-name> directly.
- See LICENSE
- Handle generated
remix package updates deliberately.
packages/remix is generated automatically in CI.
- Do not hand-edit
packages/remix/package.json or packages/remix/src/*; run the generator when generated output is required.
- When adding a new package, add it to
packages/remix/manifest.json before running the generator:
- Add one entry per export: "remix/<canonical-path>": "@remix-run/<package-name>","remix/<canonical-path>/foo": "@remix-run/<package-name>/foo". - Include type-only and runtime-condition exports that should be reachable through remix/..., such as remix/<canonical-path>/types. - Choose a domain-oriented canonical path (e.g. remix/middleware/logger, not remix/logger-middleware).
- If user asks for full surfacing, you can still update root
README.md package list when applicable.
- Validate before finishing.
- pnpm --filter @remix-run/<package-name> run typecheck - pnpm --filter @remix-run/<package-name> run test - pnpm --filter @remix-run/<package-name> run build
- Run repo lint (required):
- pnpm run lint
- Create
packages/<package-name>/.changes/ on demand and add or update a change file when requested by contribution workflow.
- For a brand-new package, the initial change file should use a
minor. filename (for example, minor.initial-release.md) so the first release bumps 0.0.0 to 0.1.0.
Templates
Use this minimal src/index.ts style:
export { createThing, type ThingOptions } from './lib/thing.ts'
Use this minimal test style:
import * as assert from '@remix-run/assert'
import { describe, it } from '@remix-run/test'
import { createThing } from './thing.ts'
describe('createThing', () => {
it('returns expected value', () => {
let result = createThing()
assert.equal(result, 'ok')
})
})