Skip to content
Recipes
Utility

Utility

mergeProps

mergeProps can be used when you want to apply a specific tailwind property to a style.

api reference, mergeProps

When to use?

For example, let's say you've created a common container component called Box. What you want to do is dynamically change only the borderColor property of the box, while maintaining the unique style you've defined for the Box. So you create a borderColor props for Box component and add the desired color, and it's applied.

import { tw, type Tailwind } from "~/tw"
 
const box = tw.style({
    display: "flex",
    alignItems: "items-center",
    borderWidth: "border",
    borderColor: "border-blue-300",
    // more box styles here...
})
 
interface BoxProps {
    children: React.ReactNode
    borderColor?: Tailwind["borderColor"]
}
export const Box = ({ children, borderColor }: BoxProps) => {
    // ass borderColor as props
    return <div className={`${box.class} ${borderColor}`}>{children}</div>
}

Now you're thinking, "I can perfectly apply any borderColor with types.". In most cases, you'll see that it works perfectly, but in some cases, it doesn't.

// border color is red-300
const Works = () => <Box borderColor="border-red-300">love</Box>
 
// border color is not gray-100
const NotWorks = () => <Box borderColor="border-gray-100">love</Box>

Problem

This happens because the order of border-color property in the css file generated by tailwind doesn't work as you wrote it down: in the className of the Box, borderColor is obviously last, but in the generated css, it's not last. The order of the css is generated in the order predefined by the tailwind framework, so it doesn't work as expected.

generated.css
/* ... */
 
.border-gray-100 {
    border-color: "~100";
}
 
/* ... */
 
.border-blue-300 {
    border-color: "~300";
}
 
/* ... */
 
.border-red-300 {
    border-color: "~300";
}
 
/* ... */

A: Merging properties

The solution is simple. If the borderColor props are passed, clear the predefined border-blue-300 property, and if not, keep it.

This is where mergeProps comes in. Unlike regular tailwind, tailwindest uses an object, so it has a key, which means we can just overwrite the value with the same key and be done with it.

import { tw, type Tailwind } from "@/tw"
 
const box = tw.style({
    display: "flex",
    alignItems: "items-center",
    borderWidth: "border",
    borderColor: "border-blue-300",
    // more box styles here...
})
 
interface BoxProps {
    children: React.ReactNode
    borderColor?: Tailwind["borderColor"]
}
export const Box = ({ children, borderColor }: BoxProps) => {
    // ass borderColor as props
    return (
        <div
            className={mergeProps(box.style, {
                borderColor,
            })}
        >
            {children}
        </div>
    )
}

B: Merging styleSheets

Or just merge all the styles with your Tailwindest type.

interface UniversalTextProps {
    children: React.ReactNode
    tw?: Tailwindest
}
const UniversalText = ({ children, tw }: UniversalTextProps) => (
    <p
        className={tw.mergeProps(
            text.style,
            tw //   override new styleSheet
        )}
    >
        {children}
    </p>
)

Now UniversalText is fully customizable via tw props.

const Some = (
    <UniversalText
        tw={{
            fontFamily: "font-mono",
        }}
    >
        It is UniversalText
    </UniversalText>
)

In this way, you don't need to install classname merging packages like tailwind-merge.