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.
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.
packages/<package-name>/.package.jsontsconfig.jsontsconfig.build.jsonCHANGELOG.mdREADME.mdLICENSE.changes/README.mdsrc/CHANGELOG.md with ## Unreleased as the first section to indicate changes are not released yet.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>#readmefiles:
LICENSEREADME.mddistsrc!src/**/*.test.tsbuild: tsgo -p tsconfig.build.jsonclean: git clean -fdXprepublishOnly: pnpm run buildtest: node --disable-warning=ExperimentalWarning --testtypecheck: tsgo --noEmit"@types/node": "catalog:""@typescript/native-preview": "catalog:"keywords like existing packages (short, lowercase, feature-focused).src entry files only.exports, map each public subpath to a dedicated file in src../package.json.publishConfig.exports with dist output:
types: ./dist/<entry>.d.tsdefault: ./dist/<entry>.jssrc file that re-exports from src/lib.
./foo -> src/foo.ts -> export { ... } from './lib/foo.ts'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"]
}
src/<entry>.ts for public entry pointssrc/lib/*.ts for implementationsrc/lib/*.test.ts for tests (colocated with implementation)import * as assert from 'node:assert/strict'import { describe, it } from 'node:test'describe().import type { ... } and export type { ... } for types..ts extensions in relative imports.let for locals; use const only at module scope.var.method() {}) instead of arrow properties.#private members.# <package-name>## Features## Installation## Usage## Related Packages (if applicable)## Licenseremix package.npm i remix
npm i remix <peer-dependency>
Usage examples must always import from remix package exports, not from @remix-run/<package-name> directly.
License section format:
See [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)remix package in PRs.packages/remix is generated automatically in CI.packages/remix/package.json or packages/remix/src/* in new pull requests.packages/remix/.changes/* change files in new pull requests.README.md package list when applicable.pnpm --filter @remix-run/<package-name> run typecheckpnpm --filter @remix-run/<package-name> run testpnpm --filter @remix-run/<package-name> run buildpnpm run lintpackages/<package-name>/.changes/ when requested by contribution workflow.minor. filename (for example, minor.initial-release.md) so the first release bumps 0.0.0 to 0.1.0.packages/remix/.changes/; remix package updates are CI-generated.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')
})
})