Remix add-package
Create or align a package in the Remix monorepo to match existing package conventions. Use when adding a brand new package under packages/, or when fixing an existing package's structure, test setup, TypeScript/build config, code style, and README layout to match the rest of Remix 3.
install
source · Clone the upstream repo
git clone https://github.com/remix-run/remix
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/remix-run/remix "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.agents/skills/add-package" ~/.claude/skills/remix-run-remix-add-package && rm -rf "$T"
manifest:
.agents/skills/add-package/SKILL.mdsource content
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.jsontsconfig.jsontsconfig.build.jsonCHANGELOG.mdREADME.mdLICENSE.changes/README.mdsrc/
- For new packages, start
withCHANGELOG.md
as the first section to indicate changes are not released yet.## Unreleased
- Set up
using monorepo conventions.package.json
- Use:
:name@remix-run/<package-name>
(for brand-new packages):version"0.0.0"
:type"module"
:license"MIT"
:repository.directorypackages/<package-name>
:homepagehttps://github.com/remix-run/remix/tree/main/packages/<package-name>#readme
- Include
:filesLICENSEREADME.mddistsrc!src/**/*.test.ts
- Add standard scripts:
:buildtsgo -p tsconfig.build.json
:cleangit clean -fdX
:prepublishOnlypnpm run build
:testnode --disable-warning=ExperimentalWarning --test
:typechecktsgo --noEmit
- Use baseline dev dependencies:
"@types/node": "catalog:""@typescript/native-preview": "catalog:"
- Add
like existing packages (short, lowercase, feature-focused).keywords
- Define exports with
entry files only.src
- In
, map each public subpath to a dedicated file inexports
.src - Always include
../package.json - Mirror each export in
withpublishConfig.exports
output:dist
:types./dist/<entry>.d.ts
:default./dist/<entry>.js
- Rule: every export must have a
file that re-exports fromsrc
.src/lib- Example: export
->./foo
->src/foo.tsexport { ... } from './lib/foo.ts'
- Example: export
- 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.
- Structure source as:
for public entry pointssrc/<entry>.ts
for implementationsrc/lib/*.ts
for tests (colocated with implementation)src/lib/*.test.ts
- Tests use Node's built-in test runner:
import * as assert from 'node:assert/strict'import { describe, it } from 'node:test'
- Keep tests IDE-friendly:
- Do not generate tests with loops/conditionals inside
.describe()
- Do not generate tests with loops/conditionals inside
- Follow monorepo code style rules while implementing.
- Use
andimport type { ... }
for types.export type { ... } - Include
extensions in relative imports..ts - Prefer
for locals; uselet
only at module scope.const - 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 (
) instead of arrow properties.method() {} - Use native class fields and
members.#private - Avoid Node-specific APIs when Web APIs are available.
- Write README in the same style and section order as existing packages.
- Start with:
# <package-name>- One short paragraph describing purpose.
- Typical section order:
## Features## Installation## Usage- Optional deep-dive sections (only if needed)
(if applicable)## Related Packages## License
- Installation instructions must always include installing the
package.remix - 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
package exports, not fromremix
directly.@remix-run/<package-name> -
License section format:
See [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)
- Do not manually update the generated
package in PRs.remix
is generated automatically in CI.packages/remix- Do not manually edit
orpackages/remix/package.json
in new pull requests.packages/remix/src/* - Do not add
change files in new pull requests.packages/remix/.changes/* - If user asks for full surfacing, you can still update root
package list when applicable.README.md
- Validate before finishing.
- Run package checks:
pnpm --filter @remix-run/<package-name> run typecheckpnpm --filter @remix-run/<package-name> run testpnpm --filter @remix-run/<package-name> run build
- Run repo lint (required):
pnpm run lint
- Add or update a change file under
when requested by contribution workflow.packages/<package-name>/.changes/ - For a brand-new package, the initial change file should use a
filename (for example,minor.
) so the first release bumpsminor.initial-release.md
to0.0.0
.0.1.0 - Exception: do not add a change file under
;packages/remix/.changes/
package updates are CI-generated.remix
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 'node:assert/strict' import { describe, it } from 'node:test' import { createThing } from './thing.ts' describe('createThing', () => { it('returns expected value', () => { let result = createThing() assert.equal(result, 'ok') }) })