Styled Components For Tailwind?!
While working on a recent side project, I had the idea to build a simple API like styled components that works with Tailwind. The idea is given a list of class names, create a component that you can easily share without writing all the normal boilerplate.
The goal is something that looks like this:
const Text = styled.p("text-sans text-lg font-light")
What appeals to me about this is in one line I have a reusable component
with automatic prop types inferred from the tag I selected, and class names
are automatically merged if you add a className
prop when using the
component.
Let’s see the implementation of this styled
function!
import { createElement } from "react"
import { twMerge } from "tailwind-merge"
const elements = [
"a",
"button",
"div",
"p",
// See the link below for the full list of properties
// https://gist.github.com/mskelton/61ea38abcae6473b9cafe777b6a2ad60#file-styled-ts-L4-L117
] as const
type SupportedHTMLElements = (typeof elements)[number]
type StyledComponent<T extends SupportedHTMLElements> = (
classes: string,
) => {
(props: React.ComponentPropsWithoutRef<T>): JSX.Element
}
type Styled = {
<T extends SupportedHTMLElements>(tag: T): StyledComponent<T>
} & {
[T in SupportedHTMLElements]: StyledComponent<T>
}
/** Create a styled component for the given HTML tag. */
export const styled = (<T extends SupportedHTMLElements>(tag: T) =>
(classes) => {
return function StyledComponent({ children, className, ...props }) {
return createElement(
tag,
{
className: className ? twMerge(classes, className) : classes,
...props,
},
children,
)
}
}) as Styled
// Add the shorthand methods for each element we support (e.g., styled.p, styled.div, etc.)
for (const element of elements) {
styled[element] = styled(element)
}