@atomic-design/di
Version:
Lightweight, type-safe dependency inversion utilities for Atomic Design architectures in React
148 lines (99 loc) • 4.28 kB
Markdown
# @atomic-design/di
> **Dependency Injection** utilities for Atomic Design contexts in React.
## Overview
`@atomic-design/di` provides a lightweight, type-safe dependency injection system tailored for Atomic Design architectures. It enables:
* **Context-based injection** of styles, atoms, molecules, and custom services into React components.
* **Named variants**: Swap implementations at runtime (e.g., feature flags, themes).
* **Server-ready components**: Support for SSR/CSR via React Server Components and Suspense.
* **Automatic dependency wiring**: Components don’t need manual prop drilling for shared dependencies.
## Installation
```bash
npm install @atomic-design/di
# or
yarn add @atomic-design/di
```
## Key Concepts
### Spaces & Variants
* **Space**: A logical group of dependencies (e.g., `styles`, `atoms`, `molecules`, or your own service spaces).
* **Variant**: A named version of a space (e.g., `default`, `v1`, `v2`) that can provide different implementations.
### Hooks & Providers
* **`useAtomic()`**: Hook to access the merged dependencies for the current React tree.
* **`AtomicProvider`**: React component to set variants for a subtree.
* **`getServerReadyComponent(space, name)`**: Wraps a component for server rendering without needing `deps` prop.
### Automatic Injection
* **`extractComponentDeps()`**: Wraps a set of components so they automatically receive a `deps` prop from context.
* **`blankDeps()`**: Utility to create a stub dependency context (useful for testing or simple values).
## Usage
### Creating a Variants Context
```tsx
import { createVariantsContext, extractComponentDeps, blankDeps } from '@atomic-design/di';
import { usePathname } from 'next/navigation';
// Define how each space is loaded (async or sync)
const atomicContext = {
styles: async () => blankDeps((await import('@atomic-design/styles-base')).stylesContext.styles),
atoms: async () => extractComponentDeps((await import('@atomic-design/atoms-base')).atomsContext.atoms),
molecules: async () => extractComponentDeps((await import('@atomic-design/molecules-base')).moleculesContext.molecules),
// Custom helper space
moleculesHelpers: () => blankDeps({
useIsActiveLink: (href?: string) => {
const pathname = usePathname() || '/';
return href ? (href === '/' ? pathname === href : pathname.startsWith(href)) : false;
}
})
};
// Optional variants for a custom "contextName" space
const variantsBySpace = {
contextName: {
v1: () => blankDeps('Variant 1'),
v2: () => blankDeps('Variant 2'),
}
};
const [useAtomic, AtomicProvider, getServerReadyComponent] =
createVariantsContext(atomicContext, variantsBySpace);
```
### Providing Variants
```tsx
<AtomicProvider space="contextName" variant="v1">
<App />
</AtomicProvider>
```
You can nest multiple providers to switch variants for subtrees.
### Using Dependencies in Components
```tsx
import React from 'react';
function MyButton(props) {
const { atoms: { Button } } = useAtomic();
return <Button {...props}>Click me</Button>;
}
```
### Server-Ready Components
Instead of manually wiring `deps`, you can export:
```tsx
export const Link = getServerReadyComponent('molecules', 'Link');
```
Then in your UI code:
```tsx
<Link href="/about">About us</Link>
```
## API Reference
### `createVariantsContext(context, variantsBySpace)`
* **`context`**: Record of space loaders (`() => WrappedPromise<Context, AllSpaces>`).
* **`variantsBySpace`**: Optional record defining named variants per space.
**Returns**: `[useAtomic, AtomicProvider, getServerReadyComponent]`
---
### `extractComponentDeps(components)`
Wraps components so each receives `deps` automatically from the nearest `AtomicProvider`.
* **`components`**: Object mapping names to React components expecting a `deps` prop.
**Returns**: `{ output: wrappedComponents, setDepsHook }`
---
### `blankDeps(output)`
Creates a stub context with a fixed `output` and no-op `setDepsHook`.
## Contributing
1. Fork the repo.
2. Create your feature branch: `git checkout -b feature/foo`.
3. Commit your changes.
4. Push to the branch.
5. Open a Pull Request.
Please follow existing code conventions (TypeScript, React JSX, ESLint, Prettier).
## License
[MIT](LICENSE)