Tiny, zero-CSS-in-JS styled()
for React — write Tailwind classes with a familiar styled-components / emotion API.
What it is: A tiny utility that returns React components from tagged templates of Tailwind classes, just like styled-components
/@emotion/styled
— but without runtime styles, no style tags, and no Babel plugins. It only concatenates class names.
What it’s not: A CSS-in-JS engine. There’s no style injection or compiler; Tailwind does the styling, you get a clean component API.
—
- Minimal: tiny surface area, no config, no runtime CSS
- Familiar:
styled.div
...and `styled(Component)`...
APIs - Composable: merge incoming
className
+ computed classes - Typed: full TypeScript inference for intrinsic tags and custom components
- Polymorphic: change the underlying tag via
as
- React-compatible: works from React 16.8+ (hooks + forwardRef)
- Peer deps:
react
(>= 16.8),tailwindcss
(>= 3)
# with bun
bun add @hamcker/styled-tailwind
# or with npm / pnpm / yarn
npm i @hamcker/styled-tailwind
import { styled } from "@hamcker/styled-tailwind";
// 1) Intrinsic element
const MyDiv = styled.div`bg-blue`;
// <MyDiv>hello</MyDiv> -> <div class="bg-blue">hello</div>
// 2) Props-driven classes
const Box = styled.div<{ primary?: boolean }>`
p-4 rounded
${(p) => (p.primary ? "bg-blue-600 text-white" : "bg-gray-100")}
`;
// 3) Custom component
type FancyProps = { title: string; className?: string; children?: React.ReactNode };
function Fancy({ title, className, children }: FancyProps) {
return (
<section className={className}>
<h2>{title}</h2>
{children}
</section>
);
}
const StyledFancy = styled(Fancy)`border p-2 shadow`;
// 4) Polymorphic via `as`
const Linky = styled.div`underline`;
// <Linky as="a" href="#">link</Linky> -> <a class="underline" href="#">link</a>
- JavaScript requires either a function call or a tagged template; syntax like
styled.divbg-blue
is not valid JS. - This utility mirrors
styled-components
/@emotion/styled
with backticks:styled.div
bg-blue``.
styled.tag\
...`: for any HTML tag, e.g.
styled.div,
styled.button`.styled(Component)\
...``: wrap your own component.- Interpolations: plain strings/numbers, falsy (
null | undefined | false
), or(props) => string | number | falsy
. className
merge: incomingclassName
is concatenated with computed classes.- Polymorphic: optional
as
prop to swap the underlying element. - Ref forwarding: components are
React.forwardRef
-aware.
cx(...parts)
: safe class concatenation.compileClasses(strings, exprs, props)
: turns a tagged template + interpolations into a final class string.
- Inference for intrinsic tags and custom components.
- Add prop types:
styled.div<{ primary?: boolean }>
. as
prop is typed asReact.ElementType
for flexibility.
- Run tests:
bun test
- Tests use Bun’s built-in
bun:test
and assert on returned React elements.
- No style sheet manipulation — class strings are computed and attached once.
- SSR works out of the box; output is plain React elements with
className
.
- Like
styled-components
or@emotion/styled
, but outputs Tailwind classes and doesn’t inject CSS. - No Babel/runtime styling; bring your own Tailwind setup for styles.
bun run build
outputs ESM (dist/index.js
), CJS (dist/index.cjs
), and types (dist/index.d.ts
).
MIT © Hamcker