# Configuring Path Aliases (#/*) for Imports in OpenCut package.json

> Learn how OpenCut configures path aliases with #/* in package.json and tsconfig.json for seamless imports mapping to the src directory.

- Repository: [OpenCut.app/OpenCut](https://github.com/OpenCut-app/OpenCut)
- Tags: how-to-guide
- Published: 2026-06-23

---

**OpenCut uses a dual-configuration strategy where the `#/*` alias is defined in both [`tsconfig.json`](https://github.com/OpenCut-app/OpenCut/blob/main/tsconfig.json) for TypeScript compilation and [`package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/package.json) for ESM runtime resolution, mapping the virtual prefix to the `src` directory.**

The OpenCut video editing application uses modern ES module path aliasing to maintain clean import statements across its React-based web interface. By configuring the `#/*` pattern in [`apps/web/package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/package.json) alongside the TypeScript configuration, the project eliminates brittle relative paths like `../../../lib/utils` in favor of absolute-style imports. This setup leverages Node.js ESM import maps and Vite's bundler resolution to work seamlessly during both development and production builds.

## Understanding the Dual Configuration Strategy

OpenCut implements the `#/*` alias through two complementary configuration files that must remain synchronized. This dual approach ensures that TypeScript can locate modules during compilation while the Node.js ESM loader resolves them correctly at runtime.

### TypeScript Configuration (tsconfig.json)

The TypeScript compiler relies on the `paths` mapping in [`apps/web/tsconfig.json`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/tsconfig.json) to understand the alias during type checking and IDE autocomplete operations. The configuration maps the `#/*` pattern to the `./src/*` directory:

```json
{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "paths": {
      "#/*": ["./src/*"],
      "@/*": ["./src/*"]
    }
  }
}

```

The `moduleResolution: "bundler"` setting is critical here, as it enables TypeScript to resolve import maps defined in [`package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/package.json) when working with modern build tools like Vite.

### ESM Import Map (package.json)

The runtime resolution depends on the `imports` field in [`apps/web/package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/package.json), which creates a Node.js ESM import map. This field mirrors the TypeScript configuration:

```json
{
  "imports": {
    "#/*": "./src/*"
  }
}

```

When Vite processes the application, it reads this import map to transform bare specifiers like `#/lib/utils.ts` into actual file system paths relative to the package root. Both configurations must target the same `./src/*` directory to prevent runtime errors that would occur if TypeScript and the bundler resolved modules differently.

## Setting Up Path Aliases in OpenCut

To configure or modify the `#/*` alias in your OpenCut installation, follow these implementation steps:

1. **Update the TypeScript configuration** in [`apps/web/tsconfig.json`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/tsconfig.json) by adding or modifying the `paths` object under `compilerOptions`:

   ```json
   {
     "compilerOptions": {
       "paths": {
         "#/*": ["./src/*"]
       }
     }
   }
   ```

2. **Declare the import map** in [`apps/web/package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/package.json) using the `imports` field at the root level:

   ```json
   {
     "imports": {
       "#/*": "./src/*"
     }
   }
   ```

3. **Restart the development server** after saving both files. Vite reads the [`package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/package.json) import map only during initialization, so hot module replacement will not detect changes to the alias configuration.

## Practical Usage Examples

With the alias active, components throughout `apps/web/src/components/ui/` import utilities using the `#/` prefix instead of relative traversal. The `cn` utility function from [`apps/web/src/lib/utils.ts`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/src/lib/utils.ts) is imported consistently across multiple components:

```typescript
// In apps/web/src/components/ui/tooltip.tsx
import { cn } from "#/lib/utils.ts"

// In apps/web/src/components/ui/button.tsx  
import { cn } from "#/lib/utils.ts"

```

The sidebar component demonstrates complex aliasing patterns by importing multiple internal modules:

```typescript
// In apps/web/src/components/ui/sidebar.tsx
import { cn } from "#/lib/utils.ts"
import { useIsMobile } from "#/hooks/use-mobile.ts"

```

These statements resolve to [`apps/web/src/lib/utils.ts`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/src/lib/utils.ts) and [`apps/web/src/hooks/use-mobile.ts`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/src/hooks/use-mobile.ts) respectively, regardless of the component's depth in the directory tree. This eliminates the need for calculations like `../../../../lib/utils` when components are nested deeply within the UI hierarchy.

## Benefits of Using the #/* Alias

Adopting the `#/*` convention in OpenCut provides three primary advantages for codebase maintenance:

- **Refactor-resistant imports**: Moving a component from `apps/web/src/components/ui/` to `apps/web/src/components/features/` requires zero import statement changes, as the `#/` prefix remains valid from any location within `src`.
- **Tooling consistency**: Because both [`tsconfig.json`](https://github.com/OpenCut-app/OpenCut/blob/main/tsconfig.json) and [`package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/package.json) define identical mappings, TypeScript language services in VS Code and the Vite bundler resolve modules to the same physical files, preventing "module not found" discrepancies between development and build time.
- **Clear dependency boundaries**: The `#` prefix immediately signals internal project imports versus external npm packages, making it easier to distinguish between first-party utilities and third-party dependencies during code reviews.

## Summary

- OpenCut configures the `#/*` path alias in both [`apps/web/tsconfig.json`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/tsconfig.json) (TypeScript `paths`) and [`apps/web/package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/package.json) (ESM `imports`).
- Both files map `#/*` to `./src/*`, enabling imports like `import { cn } from "#/lib/utils.ts"`.
- The configuration requires `moduleResolution: "bundler"` in TypeScript to work correctly with Vite.
- Changes to either configuration file require a full dev server restart to take effect.
- The alias appears throughout the UI components, including [`tooltip.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/tooltip.tsx), [`button.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/button.tsx), and [`sidebar.tsx`](https://github.com/OpenCut-app/OpenCut/blob/main/sidebar.tsx).

## Frequently Asked Questions

### Why does OpenCut use the #/* prefix instead of @/*?

OpenCut maintains both `#/*` and `@/*` aliases that point to the same `./src/*` directory according to the [`tsconfig.json`](https://github.com/OpenCut-app/OpenCut/blob/main/tsconfig.json) paths configuration. The `#` prefix follows the Node.js convention for internal package imports as defined in the ESM import maps specification, while `@` is a common TypeScript convention. The `#` character specifically indicates subpath imports that are private to the package, making it ideal for internal application code that should not be exposed as a public API.

### What happens if I only configure the alias in tsconfig.json but not package.json?

If you omit the `imports` field from [`apps/web/package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/apps/web/package.json), TypeScript will compile without errors and VS Code will provide correct autocomplete, but the application will fail at runtime with a "Cannot find module" error. This occurs because Vite's dev server relies on the Node.js ESM loader to resolve the `#/` prefix, and without the import map declaration, the runtime does not know where to locate the requested modules.

### Can I add additional path aliases beyond #/*?

Yes, you can extend both configuration files to support additional aliases. For example, to create a `#/components/*` alias that maps specifically to `./src/components/*`, add the entry to both [`tsconfig.json`](https://github.com/OpenCut-app/OpenCut/blob/main/tsconfig.json) paths and [`package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/package.json) imports. However, you must ensure that more specific aliases are listed before the general `#/*` wildcard in the `imports` object, as Node.js resolves imports in declaration order and stops at the first match.

### Does this configuration work with OpenCut's production build?

The `#/*` alias functions identically in production builds because Vite processes the [`package.json`](https://github.com/OpenCut-app/OpenCut/blob/main/package.json) import map during both development and bundling phases. The build pipeline statically analyzes the ESM imports and replaces the `#/` specifiers with relative paths or bundled module IDs. As long as both configuration files remain synchronized, the alias resolution works consistently across `vite dev`, `vite build`, and preview modes.