# How to Leverage the Shared Package for Common Code and Types in Chrome Extensions

> Simplify your Chrome extensions by leveraging the shared package for common code and types. Import utilities, types, and components easily with @extension/shared.

- Repository: [JongHak Seo/chrome-extension-boilerplate-react-vite](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The `@extension/shared` workspace provides a centralized library of utilities, TypeScript definitions, React hooks, and higher-order components that you can import into any Chrome extension entry point using the `@extension/shared` path alias.**

The `chrome-extension-boilerplate-react-vite` repository uses a monorepo architecture where the `packages/shared` directory contains code reused across popups, content scripts, side panels, and background workers. By consolidating common logic into this dedicated package, you eliminate duplication and ensure consistent behavior throughout your extension.

## Configuring the @extension/shared Path Alias

The boilerplate establishes a project-wide alias that maps `@extension/shared` to the `packages/shared/src` directory. This configuration exists in the root [`tsconfig.json`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/tsconfig.json) and is inherited by every package.

In [`packages/shared/tsconfig.json`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/shared/tsconfig.json), the compiler resolves the alias as follows:

```json
{
  "compilerOptions": {
    "paths": {
      "@extension/shared": ["../shared/src"]
    }
  }
}

```

Because both TypeScript and Vite recognize this mapping, you can import symbols using the absolute alias rather than brittle relative paths like `../../../../packages/shared`. The build system tree-shakes unused exports, ensuring each entry point bundles only the code it actually references.

## Core Utilities and Constants

The shared package exports helper functions and constants from `packages/shared/lib/utils/` and [`packages/shared/const.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/shared/const.ts).

**Colorful Logger and Constants**  
[`lib/utils/colorful-logger.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/lib/utils/colorful-logger.ts) provides a styled logging utility, while [`const.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/const.ts) defines the `COLORS` palette and related `ColorType`. These ensure consistent visual feedback across development and production builds.

**Shadow DOM Initialization**  
For content scripts that inject UI into web pages, [`lib/utils/init-app-with-shadow.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/lib/utils/init-app-with-shadow.ts) exports `initAppWithShadow`. This function creates an isolated Shadow DOM root, protecting your React components from the host page's CSS and JavaScript.

## Global Type Definitions

Type safety across extension components relies on centralized type definitions in [`packages/shared/lib/utils/types.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/shared/lib/utils/types.ts).

**ManifestType and ColorType**  
These interfaces enforce valid Chrome Extension Manifest V3 structures and restrict color prop values to the predefined palette:

```ts
import type { ManifestType, ColorType } from '@extension/shared';

const manifest: ManifestType = {
  manifest_version: 3,
  name: 'My Extension',
  version: '1.0.0',
};

type BadgeProps = {
  severity: ColorType; // 'success' | 'info' | 'error' | 'warning'
};

```

By importing these types into your popup, options page, and content scripts, you prevent type drift when the manifest schema changes.

## React Hooks for Extension State

The `useStorage` hook in [`packages/shared/lib/hooks/use-storage.tsx`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/shared/lib/hooks/use-storage.tsx) bridges React 18's concurrent features with the extension's storage API.

**Synchronizing with Chrome Storage**  
This hook accepts any storage object implementing `BaseStorageType<T>` from `@extension/storage` and returns a reactive value:

```tsx
import { useStorage } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';

export default function ThemeDisplay() {
  const theme = useStorage(exampleThemeStorage);
  
  return <div>Current theme: {theme}</div>;
}

```

The hook handles subscription cleanup and state synchronization automatically, eliminating boilerplate in individual components.

## Higher-Order Components for Error Handling

Consistent error boundaries and loading states are critical for extension UI reliability. The shared package provides two HOCs in `packages/shared/lib/hoc/`.

**withSuspense**  
Wraps components to provide a default fallback UI during lazy loading, implemented in [`with-suspense.tsx`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/with-suspense.tsx).

**withErrorBoundary**  
Catches JavaScript errors in child components and displays a recovery UI, defined in [`with-error-boundary.tsx`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/with-error-boundary.tsx).

Compose them to add production-ready error handling:

```tsx
import { withErrorBoundary, withSuspense } from '@extension/shared';
import MyComponent from './MyComponent';

export default withErrorBoundary(
  withSuspense(MyComponent, <div>Loading...</div>),
  <div>Error loading component</div>
);

```

## Practical Implementation Examples

### Accessing Storage in Content Scripts

Combine the shared hook with the storage package to persist user preferences:

```tsx
// pages/content-ui/src/App.tsx
import { useStorage } from '@extension/shared';
import { exampleThemeStorage } from '@extension/storage';

export default function App() {
  const theme = useStorage(exampleThemeStorage);
  return <div className={theme}>Content UI</div>;
}

```

### Mounting Isolated UI with Shadow DOM

When injecting React into arbitrary web pages, use `initAppWithShadow` to avoid CSS conflicts:

```tsx
import { initAppWithShadow } from '@extension/shared';
import App from './App';

initAppWithShadow(App, {
  style: `body { margin: 0; font-family: system-ui; }`,
});

```

This function, defined in [`packages/shared/lib/utils/init-app-with-shadow.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/shared/lib/utils/init-app-with-shadow.ts), creates a hidden container, attaches a Shadow Root, injects the optional stylesheet, and renders your component inside the isolated DOM.

### Reusing Types Across Packages

Import shared types into your storage implementations or page components to maintain consistency:

```ts
// packages/storage/lib/impl/example-theme-storage.ts
import type { ManifestType } from '@extension/shared';

// Ensure storage keys match manifest permissions
const manifest: ManifestType = chrome.runtime.getManifest() as ManifestType;

```

## Real-World Usage Patterns

The boilerplate demonstrates shared package consumption across multiple entry points:

- **Side Panel**: [`pages/side-panel/src/SidePanel.tsx`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/pages/side-panel/src/SidePanel.tsx) imports `PROJECT_URL_OBJECT`, `useStorage`, `withErrorBoundary`, and `withSuspense`
- **Popup**: [`pages/popup/src/Popup.tsx`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/pages/popup/src/Popup.tsx) uses the same HOCs and hooks for consistent behavior
- **Options Page**: [`pages/options/src/Options.tsx`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/pages/options/src/Options.tsx) leverages shared types for form validation
- **Content UI**: [`pages/content-ui/src/matches/example/index.tsx`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/pages/content-ui/src/matches/example/index.tsx) calls `initAppWithShadow` to mount the React tree

All re-exports flow through `packages/shared/index.mts`, which aggregates exports from [`lib/hooks/index.js`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/lib/hooks/index.js), [`lib/hoc/index.js`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/lib/hoc/index.js), [`lib/utils/index.js`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/lib/utils/index.js), and [`const.js`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/const.js).

## Summary

- **Import using `@extension/shared`** to access utilities without relative path traversal.
- **Use `useStorage`** to synchronize React components with Chrome storage areas using React 18 concurrent features.
- **Wrap components with `withSuspense` and `withErrorBoundary`** to standardize loading and error states across all extension pages.
- **Mount content script UI via `initAppWithShadow`** to isolate your CSS from the host website.
- **Reference `ManifestType` and `ColorType`** from [`lib/utils/types.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/lib/utils/types.ts) to enforce consistent typing for manifest data and theme values.

## Frequently Asked Questions

### How do I add a new utility function to the shared package?

Create the function in the appropriate subdirectory of `packages/shared/lib/` (e.g., `utils/`, `hooks/`), then export it from the corresponding [`index.js`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/index.js) file. Finally, ensure `packages/shared/index.mts` re-exports the symbol so consumers can import it via `@extension/shared`.

### Can I import `@extension/shared` into the background service worker?

Yes. The path alias works in any TypeScript file within the monorepo, including `pages/background/src/`. However, avoid importing React-specific exports like `useStorage` or `withSuspense` into the service worker context, as they depend on the React runtime which is unavailable in background scripts. Import only utility functions and types.

### Does using the shared package increase the bundle size of every entry point?

No. The Vite build configuration tree-shakes unused exports during the compilation of each entry point (popup, content script, etc.). Only the specific functions, types, and components you import are included in the final bundle, resulting in zero runtime cost for unused shared code.

### Where should I define shared constants like API endpoints or feature flags?

Place shared constants in [`packages/shared/const.ts`](https://github.com/jonghakseo/chrome-extension-boilerplate-react-vite/blob/main/packages/shared/const.ts) or create a new file in `packages/shared/lib/utils/` and export it through the package index. This ensures all extension components reference the same values, preventing configuration drift between the popup and content scripts.