Get started
Fabricator UI

Search documentation

Search pages, components, blocks, styles and colours.

Package Imports

PreviousNext

Configure Fabricator UI with package.json imports.

The Fabricator CLI supports package imports for installing components, rewriting imports, and resolving third-party registries.

Package imports let you use private #... import aliases from your package.json instead of compilerOptions.paths in tsconfig.json.

Example

You configure imports in your package.json:

package.json
{
  "imports": {
    "#components/*": "./src/components/*.tsx",
    "#lib/*": "./src/lib/*.ts",
    "#hooks/*": "./src/hooks/*.ts"
  }
}

Then import generated components using #... specifiers:

import { Button } from "#components/ui/button"
import { cn } from "#lib/utils"

App

For Next.js, Vite, and TanStack Start apps that install components into the same workspace.

Configure package.json

Add imports for the CLI install targets.

package.json
{
  "imports": {
    "#components/*": "./src/components/*.tsx",
    "#lib/*": "./src/lib/*.ts",
    "#hooks/*": "./src/hooks/*.ts"
  }
}

If your app does not use a src directory, remove src/ from the targets. For example:

package.json
{
  "imports": {
    "#components/*": "./components/*.tsx",
    "#lib/*": "./lib/*.ts",
    "#hooks/*": "./hooks/*.ts"
  }
}

Configure TypeScript

Enable package import resolution.

tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "resolvePackageJsonImports": true
  }
}

You do not need compilerOptions.paths for these aliases.

Configure components.json

Use the same #... roots in components.json.

components.json
{
  "aliases": {
    "components": "#components",
    "ui": "#components/ui",
    "lib": "#lib",
    "hooks": "#hooks",
    "utils": "#lib/utils"
  }
}

The ui alias uses #components/ui. It is still covered by the #components/* import in package.json.

The utils alias uses #lib/utils. It is covered by #lib/*, so you do not need a separate #utils import.

Monorepo

In a monorepo, use package imports for files inside each package and package exports for files shared across workspaces.

For an app workspace:

apps/web/package.json
{
  "name": "web",
  "private": true,
  "imports": {
    "#components/*": "./src/components/*.tsx",
    "#lib/*": "./src/lib/*.ts",
    "#hooks/*": "./src/hooks/*.ts"
  },
  "dependencies": {
    "@workspace/ui": "workspace:*"
  }
}
apps/web/components.json
{
  "aliases": {
    "components": "#components",
    "ui": "@workspace/ui/components",
    "lib": "#lib",
    "hooks": "#hooks",
    "utils": "@workspace/ui/lib/utils"
  }
}

For the shared UI package:

packages/ui/package.json
{
  "name": "@workspace/ui",
  "private": true,
  "imports": {
    "#components/*": "./src/components/*.tsx",
    "#lib/*": "./src/lib/*.ts",
    "#hooks/*": "./src/hooks/*.ts"
  },
  "exports": {
    "./globals.css": "./src/styles/globals.css",
    "./components/*": "./src/components/*.tsx",
    "./lib/*": "./src/lib/*.ts",
    "./hooks/*": "./src/hooks/*.ts"
  }
}
packages/ui/components.json
{
  "aliases": {
    "components": "#components",
    "ui": "#components",
    "lib": "#lib",
    "hooks": "#hooks",
    "utils": "#lib/utils"
  }
}

When you run add from apps/web, app-local files use #... imports and shared UI files are imported from @workspace/ui.

import { Button } from "@workspace/ui/components/button"
import { LoginForm } from "#components/login-form"

File extensions

The target pattern in package.json#imports controls whether generated imports include file extensions.

package.json
{
  "imports": {
    "#components/*": "./src/components/*.tsx"
  }
}

This generates imports without extensions:

import { Button } from "#components/ui/button"

If you use a target without the extension:

package.json
{
  "imports": {
    "#components/*": "./src/components/*"
  }
}

The generated import keeps the source extension:

import { Button } from "#components/ui/button.tsx"

For most apps, use the extension in the target pattern.

Troubleshooting

If TypeScript cannot resolve a #... import, check that:

  • the specifier starts with #
  • the imports entry is in the nearest package.json
  • moduleResolution is set to bundler
  • resolvePackageJsonImports is enabled
  • the matching target exists after components are added

If a component is installed but imports still point to @/..., check that components.json uses the same #... aliases as your package imports.