Writing a module

Scaffold a module

Create a new Frigear module package in the Frigear repository, with its folder layout and package.json.

A module adds one feature, with its pages, routes, translations and tables, to any host app that lists it in modules. This page is for module authors: it creates the package, its folder layout and package.json. The helpers it uses are in Module Kit.

Create the package

From the repository root:

pnpm cli module:create my-module
pnpm install

The command downloads the Nuxt module starter into packages/my-module and names the package @frigear-nu/my-module. Match the rest, such as tsconfig, build, tests and scripts, to a sibling like packages/events. CLI lists the command's options.

Give the package a dev:stub script, never a postinstall. A postinstall would be published and run in every install. The root postinstall runs each package's dev:stub instead.

Folder layout

packages/my-module/
├── src/
│   ├── module.ts            the Nuxt module: meta, moduleDependencies, setup()
│   ├── types.ts             ModuleOptions and the other public types
│   └── runtime/
│       ├── app/             components, composables, pages (client and SSR code)
│       ├── server/          routes, plugins, utils
│       │   └── db/          schema.sqlite.ts and schema.postgresql.ts
│       ├── i18n/lang/       da.json and en.json
│       └── utils/
│           ├── index.ts     the `/utils` export: helpers other modules call in their setup()
│           └── api.ts       the `/api` export: typed path builders for the module's routes
├── test/
│   ├── fixtures/<name>/     test fixtures: small Nuxt apps that install the module
│   └── *.test.ts
├── playground/              the playground
├── .release-it.json
└── package.json

The playground is a dev app for working on this one package, started with pnpm --filter @frigear-nu/my-module dev. It is not a test fixture. Only the built dist/ is published, so keep everything a host app needs under src/runtime.

package.json

Inside the repository, exports point at the TypeScript source. On publish, publishConfig swaps in the built files. Add ./utils when other modules import helpers from yours, and ./api for your path builders.

{
  "name": "@frigear-nu/my-module",
  "type": "module",
  "main": "./src/module.ts",
  "exports": {
    ".": { "types": "./src/module.ts", "import": "./src/module.ts", "default": "./src/module.ts" },
    "./utils": {
      "types": "./src/runtime/utils/index.ts",
      "import": "./src/runtime/utils/index.ts",
      "default": "./src/runtime/utils/index.ts"
    },
    "./api": {
      "types": "./src/runtime/utils/api.ts",
      "import": "./src/runtime/utils/api.ts",
      "default": "./src/runtime/utils/api.ts"
    }
  },
  "publishConfig": {
    "exports": {
      ".": { "types": "./dist/types.d.mts", "import": "./dist/module.mjs", "default": "./dist/module.mjs" },
      "./utils": {
        "types": "./dist/runtime/utils/index.d.ts",
        "import": "./dist/runtime/utils/index.js",
        "default": "./dist/runtime/utils/index.js"
      },
      "./api": {
        "types": "./dist/runtime/utils/api.d.ts",
        "import": "./dist/runtime/utils/api.js",
        "default": "./dist/runtime/utils/api.js"
      }
    },
    "main": "./dist/module.mjs",
    "typesVersions": { "*": { ".": ["./dist/types.d.mts"] } }
  },
  "files": ["dist"]
}

Next steps

  • Setup checklist – declare the dependencies and write setup().
  • Testing – add a test fixture before the module grows.
Copyright © 2026