Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/tasty-swans-make.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ jobs:

- name: Require changeset entry
if: github.event_name == 'pull_request'
run: pnpm changeset status
run: |
git fetch origin ${{ github.base_ref }} --depth=1
pnpm changeset status --since=origin/${{ github.base_ref }}

- name: Check formatting
run: pnpm format:check
Expand Down
110 changes: 55 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ A single component factory function for polymorphic React components. Built for
import { createPolymorph } from 'react-polymorphic';

const Button = createPolymorph<'button'>(({ as: Tag = 'button', ...rest }) => {
return <Tag {...rest} />
return <Tag {...rest} />
})

const App = () => {
return <Button as="a" href="/">Home</Button>
return <Button as="a" href="/">Home</Button>
}
```

Expand Down Expand Up @@ -70,7 +70,7 @@ Polymorphic components let you keep one consistent API while swapping the render
`InferPolymorphElement`, `InferPolymorphProps`, and `InferPolymorphConfig` for advanced typing workflows.

- **TypeScript 7 Ready**
Compatible with TypeScript 5.4+ and optimized for TypeScript 7.
Compatible with TypeScript 5.4+ and optimized for TypeScript 7.

- **Built for React 19+**
Leverages React 19's handling of `ref`, eliminating `forwardRef` and convoluted type gymnastics.
Expand All @@ -96,23 +96,23 @@ Most polymorphic components are simple wrappers that combine custom props with i
import { createPolymorph } from 'react-polymorphic';

type Props = {
variant: 'primary' | 'secondary';
variant: 'primary' | 'secondary';
}

const Pane = createPolymorph<'div', Props>((props) => {
const { as: Tag = 'div', variant, className, ...rest } = props;
const variantClasses = variant === 'primary' ? 'bg-white' : 'bg-blue-50';
const { as: Tag = 'div', variant, className, ...rest } = props;
const variantClasses = variant === 'primary' ? 'bg-white' : 'bg-blue-50';

return <Tag className={`p-4 ${variantClasses} ${className}`} {...rest} />
return <Tag className={`p-4 ${variantClasses} ${className}`} {...rest} />
})

const App = () => {
return (
<>
<Pane as="section" variant="primary" aria-label="...">...</Pane>
<Pane as="aside" variant="secondary" aria-label="...">...</Pane>
</>
)
return (
<>
<Pane as="section" variant="primary" aria-label="...">...</Pane>
<Pane as="aside" variant="secondary" aria-label="...">...</Pane>
</>
)
}
```

Expand All @@ -127,20 +127,20 @@ import { useRef } from 'react';
import { createPolymorph } from 'react-polymorphic';

const Component = createPolymorph<'div'>((props) => {
const { as: Tag = 'div', ...rest } = props;
return <Tag {...rest} />
const { as: Tag = 'div', ...rest } = props;
return <Tag {...rest} />
})

const App = () => {
const ref = useRef<HTMLDivElement>(null);
const aRef = useRef<HTMLAnchorElement>(null);

return (
<>
<Component ref={ref} />
<Component as="a" ref={aRef} />
</>
)
const ref = useRef<HTMLDivElement>(null);
const aRef = useRef<HTMLAnchorElement>(null);

return (
<>
<Component ref={ref} />
<Component as="a" ref={aRef} />
</>
)
}
```

Expand All @@ -155,34 +155,34 @@ import { createPolymorph } from 'react-polymorphic';
import type { PolymorphicConfig, Unset, Inherit } from 'react-polymorphic';

type Props = {
loading: boolean;
loading: boolean;
}

type Config = PolymorphicConfig<'button', {
'aria-loading': Unset;
children: string;
href: string;
target?: '_blank';
type: Inherit;
'aria-loading': Unset;
children: string;
href: string;
target?: '_blank';
type: Inherit;
}>;

const Button = createPolymorph<'button', Props, Config>((props) => {
const { as: Tag = 'button', children, loading, ...rest } = props;
const { as: Tag = 'button', children, loading, ...rest } = props;

return (
<Tag aria-loading={loading} {...rest}>
{loading ? 'Loading...' : children}
</Tag>
)
return (
<Tag aria-loading={loading} {...rest}>
{loading ? 'Loading...' : children}
</Tag>
)
})

const App = () => {
return (
<>
<Button type="button">Calculate</Button>
<Button as="a" href="/">Back to home</Button>
</>
)
return (
<>
<Button type="button">Calculate</Button>
<Button as="a" href="/">Back to home</Button>
</>
)
}
```

Expand All @@ -201,12 +201,12 @@ import { createPolymorph } from 'react-polymorphic';
const OtherComponent = createPolymorph<'div'>(...);

const Component = createPolymorph<'div'>((props) => {
const { as: Tag = 'div', ...rest } = props;
return <Tag {...rest} />
const { as: Tag = 'div', ...rest } = props;
return <Tag {...rest} />
});

const App = () => {
return <Component as={OtherComponent} root="section" />
return <Component as={OtherComponent} root="section" />
}
```

Expand All @@ -219,9 +219,9 @@ You can extract generics from existing polymorphic components and reuse them whe
```tsx
import { createPolymorph } from 'react-polymorphic';
import type {
InferPolymorphElement,
InferPolymorphProps,
InferPolymorphConfig
InferPolymorphElement,
InferPolymorphProps,
InferPolymorphConfig
} from 'react-polymorphic';
import { TheirButton } from 'some-library';

Expand All @@ -237,8 +237,8 @@ type TheirConfig = InferPolymorphConfig<typeof TheirButton>;
type Props = { ... }

const OurButton = createPolymorph<typeof TheirButton, Props>((props) => {
const { as: Tag = TheirButton, ...rest } = props;
return <Tag {...rest} />
const { as: Tag = TheirButton, ...rest } = props;
return <Tag {...rest} />
})
```

Expand All @@ -248,14 +248,14 @@ If swapping out the base would break core behavior, preserve the base and expose

```tsx
const OurButton = createPolymorph<TheirElement, Props & TheirProps, TheirConfig>(
(props) => {
const { as = 'button', ...rest } = props;
return <TheirButton as={as} {...rest} />
}
(props) => {
const { as = 'button', ...rest } = props;
return <TheirButton as={as} {...rest} />
}
)

const App = () => {
return <OurButton as="a" />
return <OurButton as="a" />
}
```

Expand Down
Loading