Contribution guidelines for the project.
This project uses pnpm as the package manager; you must have pnpm
installed globally to run commands and tests locally.
Here’s where each type of file lives:
src/registry/base holds the main components for export.src/registry/demos holds demo components.src/registry/demos/index.ts exports the demo components.src/content holds documentation.(src/registry)
|-- base
| |-+ your-component
| |-+ your-component.tsx # Your new component
|-- hooks
| |-+ use-frame-loop.ts # Shared hooks
|-- demos
| |-+ your-component
| | |-+ your-component.tsx # Your new demo component
| |
| |-- index.ts # Exports the demo components
(src/content)
|-- en
|-- components
|-- _dir.yml
|-+ your-component
|-+ your-component.mdx # Your new component documentationEach component should have a single responsibility and reside in a single file to keep it self-contained and easy to reuse.
If some logic, like a hook, is shared across multiple components, place it in a shared folder in src/registry and reference it in the component's shared field in src/registry/index.ts. One example is the use-frame-loop hook.
const NAMED_X_CONSTANT = {...} as const
const NAMED_Y_CONSTANT = "value" as const
function helperFunction() {...}
type ComponentExampleProps = {...} & ComponentProps<'div'>
export function ComponentExample({}: ComponentExampleProps) {...}This project uses motion.dev for DOM-based animations, React Three Fiber for WebG effects, and Tailwind CSS for styling.
Vanilla JS animations such as requestAnimationFrame, Web Animations API can also be used where a library isn't needed.
Tests run automatically on every pull request. They verify that every component has its files, demo, export, and documentation in sync. Unit test per components are not required by default.
Use the pnpm scafold-new command to create a new component. This creates the empty base component, demo, empty templated MDX doc, and patches the registry and demo index automatically.
Example:
# e.g. pnpm scafold-new animated-text --category scroll
# e.g. pnpm scafold-new sparkle --category background
If you prefer to do it manually, follow the steps below.
Create a new folder in the src/registry/base folder with the name of the component:
export function ComponentName() {
return <></>;
}Create a demo component in the src/registry/demos folder and structure it how you want:
import ComponentName from "@/registry/base/component-name/component-name";
export function ComponentNameDemo() {
return <ComponentName />;
}Export the demo component in the src/registry/demos/index.ts file:
export const demos: Record<
string,
React.LazyExoticComponent<React.ComponentType>
> = {
"component-name": lazy(() => import("./component-name/component-name")),
};Add the component to the registry by including its name and dependencies in src/registry/index.ts:
export const components: TRegistryComponent[] = [
{
name: "new-component",
files: ["new-component.tsx"],
description: "My new component",
shared: ["hooks/my-shared-hook", "utils/my-shared-utility"],
dependencies: ["example-dependency-1", "example-dependency-2"],
},
];Add the metadata for the component in the frontmatter:
---
title: "New Component"
description: "My new component"
icon: "Component"
---Add a preview of the component by using the <DemoPreview /> component with the same exact name of the demo component.
<DemoPreview name="component-name" />Add the CLI installation guide by using the <InstalGuideCLI /> component with the same exact name of the base component.
<InstalGuideCLI name="component-name" />Add the manual installation guide by using the <InstalGuideManual /> component with the same exact name of the base component.
<InstalGuideManual name="component-name" />You can add interactive controls to the preview so users can tweak component props live. Pass a controls object to <DemoPreview />.
Always make sure the values match the default props of the base component:
<DemoPreview
name="component-name"
controls={{
speed: { type: "slider", value: 1, min: 0, max: 10, step: 0.1 },
color: { type: "color", value: "#ff5733" },
variant: { type: "select", value: "solid", options: ["solid", "outline"] },
enabled: { type: "boolean", value: true },
}}
/>Available control types:
| Type | Fields | Renders |
|---|---|---|
slider | value min max step | Slider |
color | value | Color picker |
select | value options | Select |
boolean | value | Toggle switch |
Export the base component's props type, then spread controls onto it:
export default function ComponentNameDemo(
controls: Partial<ComponentNameProps>,
) {
return;
}Add the API of the component by using the following table format:
| Name | Type | Default | Description |
| ----------------- | ------- | --------- | -------------------------------------------------------------------- |
| `speed` | number | `1.0` | Controls the animation speed multiplier. |
| `color` | string | `#ff5733` | Sets the primary color of the component. |Provide a detailed list of the component's credits:
[Author Name](https://example.com)
Designed the original component concept.
[Library Name](https://example.com)
Used for animation utilities within this component.