Shared UI Components
Defines reusable UI components with stable interfaces and limited application specific dependencies.
Scenario
You have a React application with a shared UI library containing reusable design-system components.
The library includes components such as:
- Button;
- Input;
- Dialog;
- Card;
- Badge.
Product features compose these primitives into feature-specific components.
You want coding agents to reuse the design system without turning the shared UI package into a home for business-specific components.
Repository Structure
web-app/
├── src/
│ ├── features/
│ │ ├── billing/
│ │ │ └── components/
│ │ │ └── upgrade-dialog.tsx
│ │ └── users/
│ └── components/
│ └── ui/
│ ├── button.tsx
│ ├── dialog.tsx
│ ├── input.tsx
│ └── card.tsx
├── tests/
└── AGENTS.md
AGENTS.md
# Project Instructions
## Shared UI
- Reuse components from `src/components/ui` when an appropriate primitive already exists.
- Check existing UI primitives before creating a new shared component.
- Keep shared UI components generic and independent of product-specific business logic.
## Feature Components
- Keep feature-specific compositions inside the owning feature.
- Do not move a component into `src/components/ui` only because it is used more than once within the same feature.
## Styling
- Follow the existing design-system variants and styling patterns.
- Extend an existing primitive when the new behavior is broadly reusable.
- Avoid duplicating an existing UI primitive with slightly different styling.
## Dependencies
- Shared UI components must not import from `src/features`.
- Feature components may import shared UI primitives.
## Validation
When modifying shared UI:
- run relevant component tests;
- run `pnpm lint`;
- run `pnpm typecheck`;
- validate important consumers when changing a shared component's public behavior.
What This Does
This establishes a dependency direction:
Shared UI
↑
Features
Feature code can depend on shared UI primitives.
Shared UI should not depend on product features.
For example:
src/components/ui/dialog.tsx
can provide a generic dialog.
The billing feature can compose it into:
src/features/billing/components/upgrade-dialog.tsx
The generic Dialog should not contain billing-specific concepts such as plans, subscriptions, or pricing.
What This Does NOT Do
The instructions do not say every repeated component must become part of the shared UI library.
Suppose the billing feature uses:
SubscriptionStatusCard
on three billing screens.
That does not automatically make it a design-system primitive.
It may still belong under:
src/features/billing/
because its meaning is specific to billing.
The file also does not prohibit extending the design system.
If several unrelated features need the same generic interaction, adding or extending a shared primitive may be appropriate.
Why These Instructions Matter
Shared component directories tend to grow quickly.
Without ownership rules, they can become a mixture of:
Button
Dialog
UserProfileCard
BillingPlanSelector
ProjectPermissionModal
Input
Now generic primitives and business components live together.
The dependency problem can become worse if:
components/ui/
starts importing from:
features/billing/
The supposed shared layer now depends on the product layer.
The instruction:
- Shared UI components must not import from `src/features`.
protects the intended dependency direction.
Key Decisions
Reuse before creating
Before adding:
secondary-button.tsx
the agent should check whether the existing:
button.tsx
already supports variants or can be extended cleanly.
Reuse does not automatically mean shared
A component can be reused several times within one feature and still belong to that feature.
Ownership matters more than raw usage count.
Shared components need broader validation
Changing a shared Button can affect many screens.
The change therefore has a larger blast radius than modifying one feature-specific component.
Keep primitives business-agnostic
A useful question is:
Could this component reasonably be used by a completely different feature?
If not, it may not belong in the shared UI layer.
When to Use This Pattern
Use this pattern when:
- a project has a design system or shared UI primitives;
- multiple features consume common components;
- duplicated UI implementations are becoming common;
- business components are leaking into shared directories;
- dependency direction between UI and features matters.
The pattern also applies when the shared UI exists as a separate workspace package rather than inside one application.