Proper UI

Monorepo integration

Consume Proper UI from a Turborepo or pnpm workspace: the workspace protocol, Tailwind source scanning, and TypeScript project references.

If you already run Proper UI's own monorepo layout (a pnpm workspace with apps/* and packages/*) or you're adding it to one of yours, @properui/ui behaves exactly like a published npm package, resolved locally instead of from the registry.

Installation

List the package as a workspace dependency of whichever app consumes it:

// apps/web/package.json
{
    "dependencies": {
        "@properui/ui": "workspace:*"
    }
}

pnpm-workspace.yaml (or the workspaces field in package.json for npm/Yarn) needs to include both the app and the package:

# pnpm-workspace.yaml
packages:
    - "apps/*"
    - "packages/*"

Run pnpm install and pnpm symlinks packages/ui into apps/web/node_modules/@properui/ui; from the app's point of view it's an ordinary dependency, so the same next.config.ts and CSS setup from the Next.js guide or Vite guide applies without changes:

// apps/web/next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
    transpilePackages: ["@properui/ui"],
};

export default nextConfig;
/* apps/web/app/globals.css */
@import "@properui/ui/styles/globals.css";

@source "../node_modules/@properui/ui/src/**/*.{ts,tsx}";

For editor tooling and incremental builds, add a TypeScript project reference from the app to the package so tsc --build understands the dependency order:

// apps/web/tsconfig.json
{
    "references": [{ "path": "../../packages/ui" }]
}
// packages/ui/tsconfig.json
{
    "compilerOptions": {
        "composite": true
    }
}

Custom alias paths

Inside packages/ui/src, every component imports its neighbors with the package's own @/ alias: that alias is local to the package and resolves against packages/ui/src, never against your app's src directory. Leave it alone; the CLI rewrites it automatically when it copies a component into a project that doesn't use the workspace package.

Your app keeps its own @/ alias pointing at its own src, and reaches Proper UI through the package's subpath exports instead of reusing that alias for the shared package:

// apps/web/tsconfig.json
{
    "compilerOptions": {
        "paths": {
            "@/*": ["./src/*"]
        }
    }
}

If you want to jump straight to the package's source while developing (skipping the built output), add a second alias that points at it explicitly:

// apps/web/tsconfig.json
{
    "compilerOptions": {
        "paths": {
            "@/*": ["./src/*"],
            "@properui/ui/*": ["../../packages/ui/src/*"]
        }
    }
}

This is a development convenience only: published consumers outside the monorepo always resolve @properui/ui/* through the package's own exports map, so keep the mapping scoped to apps that live inside this workspace.

Copying source into a shared package

The section above covers @properui/ui as a workspace dependency. If you'd rather copy source with the CLI and own it outright (see the CLI guide), you can still share the result across multiple apps by copying into a shared package once instead of into each app.

1. Point add at the shared package, not an app. Run it with --cwd set to the shared package's directory (or run it from inside that directory) so the components.json it reads, and the alias it rewrites copied imports to, are the shared package's own:

npx @properui/cli@latest init --cwd packages/ui-kit
npx @properui/cli@latest add button input select --cwd packages/ui-kit

2. Every consuming app needs its own @source line reaching into the shared package. Tailwind only scans what the stylesheet being built can reach through @source; it isn't transitive across packages, so each app repeats the line even though only one package holds the source:

/* apps/web/app/globals.css */
@import "tailwindcss";
@import "../../../packages/ui-kit/src/styles/theme.css";

@source "../../../packages/ui-kit/src/**/*.{ts,tsx}";
/* apps/admin/app/globals.css */
@import "tailwindcss";
@import "../../../packages/ui-kit/src/styles/theme.css";

@source "../../../packages/ui-kit/src/**/*.{ts,tsx}";

3. Give each app a second alias for the shared package, instead of overloading its own @/. The shared package's internal @/ (rewritten there by add, same as described above for @properui/ui itself) resolves against its own src/ and never leaves that package. An app keeps @/* pointed at its own src/ and adds an explicit, differently-named path for the shared package:

// apps/web/tsconfig.json
{
    "compilerOptions": {
        "paths": {
            "@/*": ["./src/*"],
            "@ui-kit/*": ["../../packages/ui-kit/src/*"]
        }
    }
}
import { Button } from "@ui-kit/components/base/buttons/button";

Repeat the alias entry in every app's tsconfig.json that imports from the shared package; each app resolves it independently, the same way each app needs its own @source line.

If your tool supports MCP servers, the Proper UI MCP lets the agent search screens, flows and sections and plan the install, with the CLI writing the files.

FAQs