ScreenshotNeo

BlogGuides

How to Share UI Components Across Projects

Choose a sharing model that fits your repositories and release workflow: use a workspace package, publish a versioned library, or install component source directly.

By the ScreenshotNeo team4 October 202610 min read

To share UI components across projects, match the sharing boundary to how the projects are maintained. Put a shared package in a monorepo when the applications evolve together; publish a versioned package when separate repositories need an explicit release boundary; install component source into each project when consumers should own and edit the files. Add Storybook to document and browse examples. Storybook helps developers discover components, but it does not distribute their implementation.

This guide covers the tradeoffs, setup decisions, update flow, and common failure modes for each approach. There is no single best model for every team: repository boundaries, release ownership, and how consumers should adopt changes determine the choice.

Choose a sharing model

Approach Best fit How updates reach consumers Main responsibility
Workspace package in a monorepo Apps maintained together Consumers can use coordinated workspace changes Define package boundaries, builds, and release discipline
Published package Separate repositories or independent release schedules Consumers install a released version Build, publish, version, and communicate changes
Installed component source Consumers should own and adapt component files Teams manage updates to their installed copies Decide how fixes and improvements are propagated
Storybook Teams need examples and a catalog Stories are published or composed for discovery Keep documentation and stories useful; pair with a code-sharing mechanism

A practical starting decision:

  1. If the apps and UI library are developed and reviewed together, start with a workspace package.
  2. If consumers live in separate repositories or must choose when to upgrade, publish a versioned package.
  3. If consumers need to change installed components directly, use a source-install workflow and make ownership explicit.
  4. If developers need to inspect examples, states, and usage across projects, add Storybook alongside the chosen distribution method.

Option 1: Share a workspace package in a monorepo

A workspace package gives related projects one checkout while preserving a package boundary. Applications import the UI library through its package name instead of reaching into arbitrary paths in another app. This keeps ownership and imports easier to reason about as the repository grows.

A concrete example is the shadcn/ui monorepo guide, which uses apps/web and packages/ui, directs component files into the UI workspace, and configures imports. Vercel’s Turborepo design-system example similarly includes a docs app, a core UI package, and shared TypeScript and ESLint configuration packages. These are examples of workable structures, not requirements for every monorepo.

Set up the boundary deliberately

  1. Create a package for shared UI components, with a clear package name and public entry points.
  2. Configure the package manager’s workspace so applications can depend on the local package.
  3. Decide whether consumers import source files directly or a built package entry point. Make that choice consistent with your framework, build tooling, and development workflow.
  4. Keep app-specific screens and business logic in their applications. Put only genuinely reusable components, styles, and utilities in the shared package.
  5. Set up shared TypeScript, lint, and build configuration only where it helps keep packages consistent.
  6. Document component usage and add a Storybook docs app if developers need a browsable catalog.

Monorepos make coordinated edits convenient because consumers and library code are checked out together. They do not decide package boundaries, build behavior, or release discipline for you; the team must define those parts.

Source-install workflow inside a monorepo

A source-install CLI can also place selected components directly into a UI workspace. The shadcn/ui guide describes configuring workspace paths and aliases so components, hooks, utilities, and styles land in the intended locations. Verify the generated imports and configuration against your repository layout; a CLI cannot infer every project’s conventions.

Option 2: Publish a package for separate repositories

For consumers outside the monorepo, create a library that can be built into a distributable artifact and published to a package registry. Consumers then declare a dependency and adopt explicit versions, which gives maintainers a clear release boundary.

Nx distinguishes ordinary workspace libraries from publishable libraries: an ordinary library is intended for direct use by apps in the workspace, while a publishable library is intended for distribution beyond it. The publishable generator adds a build target and creates an artifact suitable for publishing; generating it does not publish the package. The import path must be a valid package name for this workflow. See the Nx guide to publishable and buildable libraries.

Release and consume the library

  1. Choose a stable package name and define which components and styles are public.
  2. Configure a build that emits the artifacts consumers need, with the package metadata and entry points that match your chosen module formats and type setup.
  3. Build and inspect the output before release. Confirm that required styles, assets, and peer dependencies are represented in the package.
  4. Publish a version to the registry your consumers use.
  5. Have consuming projects install the selected version through their package manager, then upgrade intentionally when a new release is available.
  6. Document breaking changes and the supported upgrade path so consuming teams can plan adoption.

The benefit is independent repositories and explicit adoption. The cost is release work: maintainers must build, publish, communicate changes, and manage compatible versions. A generated publishable target is only part of that workflow.

Option 3: Install component source into each project

With source installation, a tool copies selected component files into a consumer’s source tree. The consumer can then edit those files directly and adapt them to local needs. This is useful when consumers should own their implementation rather than depend on a centrally compiled library.

The shadcn/ui CLI documents installing components into a UI workspace, updating imports, and placing application-specific files for larger blocks in the app itself. Its monorepo setup requires workspace configuration and aliases to guide where components, hooks, utilities, and styles belong. Follow the shadcn/ui monorepo guide for the CLI’s documented layout and configuration.

Source copies do not automatically stay synchronized with a central library unless the tooling provides that behavior and the team configures it. Assign ownership for updates: decide who tracks upstream changes, how fixes move between projects, and whether local modifications should be rebased, copied, or left independent.

Use Storybook for examples and discovery

Storybook complements a package or source-install workflow by making components and their states easier to explore. Its sharing guide describes publishing a Storybook, embedding stories in a site, design integrations, and composition. These approaches help teams find and understand examples; they do not make component code available to an application. See Storybook’s sharing guide.

Compose stories across Storybooks

Storybook composition lets a team browse stories from another Storybook within its own Storybook. The composed Storybook can use a different view layer or technology stack, which makes it useful for finding prior art and inspecting how other teams use a shared system. Composition is a browsing connection, not a code dependency. See Storybook composition documentation.

Compose a published package’s stories

Package composition can display stories from a published component package alongside a consumer’s stories when the package supports the integration. Storybook documents a secure connection between the publishing service and Storybook APIs, recommends publishing to Chromatic for full support, and describes configuring a Storybook URL in the published package metadata. For Chromatic-hosted Storybooks, the documentation also describes version selection. Check the current package composition guide for the supported setup.

Storybook says, “Design system authors can automatically compose their design systems inside their consumer’s Storybooks.” Treat that as a documentation and discovery capability; consumers still need a package or source workflow to use the implementation.

Plan updates, compatibility, and ownership

The sharing mechanism only works well when teams know who changes components and how those changes reach applications. Set these decisions before the library accumulates consumers:

  • Public API: Mark supported exports, styles, and component properties. Avoid making internal files an accidental dependency.
  • Change ownership: Name who reviews shared changes and who decides whether a change belongs in the shared library or in an app.
  • Adoption model: In a workspace, coordinate changes with consumers. For published packages, consumers choose versions. For copied source, consumers own their local updates unless a synchronization process is established.
  • Compatibility: Identify changes that require consumer edits and communicate them with the release or update.
  • Validation: Run the relevant build, lint, and checks across affected packages or consumers. A monorepo task graph can help coordinate these tasks, but the repository still needs to configure them.
  • Documentation: Keep examples close to the component API and show important variants and states. Storybook can make these examples discoverable.

Vercel’s Turborepo design-system example demonstrates a docs app, UI package, and shared configuration packages with build, lint, and release tasks. Use it as a reference for one setup, not as evidence that every project needs the same tools.

Common problems and fixes

Symptom Likely cause What to check or change
An app cannot resolve the shared package The package is not registered in the workspace, the dependency name differs from its package name, or the import path is wrong. Check workspace configuration, package metadata, and the consumer’s dependency declaration. Import through the intended package boundary.
A component works locally but fails after package installation The published artifact or package metadata omits files, entry points, styles, or required dependencies. Inspect the built package and its metadata before publishing; verify the consumer imports a public entry point.
A generated library cannot be published as expected A publishable generator prepared a build target but did not publish to a registry. Build the artifact, configure the registry and credentials, and run the separate release process. Confirm the package name is valid.
Installed components appear in the wrong directory The source-install CLI’s workspace paths or aliases do not match the repository. Review the CLI configuration for components, hooks, utilities, and styles, then inspect generated imports.
Consumers have divergent copies of a component Source-installed copies were edited independently and no update ownership or synchronization process exists. Decide whether to keep local ownership, manually adopt upstream fixes, or move shared changes into a package with explicit releases.
Storybook shows stories, but an app cannot import the component Storybook composition provides discovery, not implementation distribution. Add a workspace dependency, publish and install the library, or install the component source.
A composed Storybook cannot load a package’s stories The package composition integration or hosted Storybook metadata may not be configured or supported. Check the current Storybook package-composition prerequisites, package metadata, publishing-service connection, and version selection.

Performance, reliability, and cost considerations

These approaches do not have one universal runtime-performance result: the outcome depends on what the app builds and ships. Evaluate the actual consumer bundle and loading path for your stack rather than assuming a monorepo or registry choice makes a component faster. Keep package exports intentional and verify that consumers include the styles and code they need.

  • Workspace package: Coordinated development can reduce the delay between a library change and a consumer change. Reliability depends on reproducible workspace configuration and checks across affected packages.
  • Published package: A release creates a stable version consumers can select, while introducing registry, build, and release steps. Pin and upgrade versions according to the team’s dependency policy.
  • Installed source: Consumers can adapt the implementation locally, but copies can diverge. Reliability depends on clear local ownership and a plan for shared fixes.
  • Storybook: Publishing and composition make examples accessible, but depend on the configured hosting and integration. Story discovery does not replace the library’s build or distribution path.
  • Cost: The cited implementation guides do not establish a universal cost comparison. Account for your own registry, hosting, CI, maintenance, and team workflow; do not infer savings without measuring those costs in your environment.

Or skip the browser setup

If the shared component work also needs reference screenshots of deployed pages, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. AI agents can use its MCP tools to take screenshots, get page information, and capture PDFs.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed; an MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card required.

Frequently asked questions

Does Storybook share component code between projects?

No. It shares examples and discovery experiences. Use a workspace package, published package, or source-install workflow to make implementation available to projects.

Should every shared component live in one package?

Only when the components have a useful shared owner and consumers. Keep app-specific behavior with the app that owns it; split packages when ownership or release needs genuinely differ.

Can separate repositories use a monorepo workspace package?

A workspace package is organized for projects in the workspace. Separate repositories generally need a distribution mechanism such as a published package or a deliberate source-install process.

Does generating a publishable library release it automatically?

No. The generator can prepare a build target and publishable artifact. Building and publishing the package remain separate release steps.

Which approach should a small team start with?

Choose based on where the apps live and who should own updates. Apps maintained together can start with a workspace package; separate repositories need a release or source-install boundary.