Tailwind CSS vs twin.macro: The Ultimate Showdown for Next.js and Sitecore Projects
A practical guide to choosing between Tailwind CSS and twin.macro for your Next.js + Sitecore setup.
Start typing to search...
When you’re working on a Next.js project with Sitecore and TypeScript, choosing the right approach to styling can make all the difference. You want something that keeps development fast, your code clean, and lets you tackle both static and dynamic components without pulling your hair out. In this blog, I’ll break down Tailwind CSS and twin.macro—two awesome tools for styling—and help you decide which one fits your project best.
Tailwind CSS is all about making styling fast, flexible, and scalable. It lets you build UI elements quickly by applying pre-defined utility classes directly in your TSX files. When you're working with Next.js, Tailwind’s compatibility with server-side rendering (SSR) and static site generation (SSG) makes it even better because you get fast load times and a fully responsive design right out of the gate.
Now, throw TypeScript into the mix, and things get even better. TypeScript adds that extra layer of type safety to your components, making sure your props and state are well-defined. That means fewer errors and a smoother development process overall. This combo lets you fly through UI development with confidence while keeping everything clean and consistent.
In a Next.js + Sitecore project, Tailwind CSS really shines when it comes to building consistent, responsive, static layouts. It's a massive time-saver for things like headers, footers, buttons, and forms—without needing to mess around with custom CSS. For projects that rely on structured, static content, Tailwind delivers exactly what you need, without the fuss.
Example: Tailwind CSS Example in TypeScript
type HeaderProps = {
title: string;
links: { href: string; label: string }[];
};
const Header = ({ title, links }: HeaderProps) => (
<header className="bg-gray-900 w-full h-20 p-6 flex flex-row gap-20">
<h2 className="text-white heading-2">{title}</h2>
<nav>
<ul className="flex flex-row gap-6">
{links.map((link, index) => (
<li key={index}>
<a className="text-white body-medium" href={link.href}>
{link.label}
</a>
</li>
))}
</ul>
</nav>
</header>
);
export default Header;
This approach works great for static content, but when you start needing to switch up styles dynamically based on props, state, or Sitecore personalization rules, you may run into challenges. This is where twin.macro can be your new best friend.
twin.macro takes everything you love about Tailwind CSS and pairs it with CSS-in-JS libraries like styled-components or emotion. The result? You get to apply Tailwind’s utility classes dynamically, based on props, state, or whatever other logic you’re using in your TypeScript components without losing any of the simplicity that makes Tailwind great.
Example: Dynamic Styling with twin.macro
import tw from 'twin.macro';
type CardProps = {
title: string;
description: string;
isFeatured?: boolean;
};
const Card = ({ title, description, isFeatured }: CardProps) => (
<div
css={[
tw`p-4 rounded-md bg-white shadow-md`, // Base styles
isFeatured && tw`bg-yellow-100`, // Conditionally apply styles
]}
>
<h2>{title}</h2>
<p>{description}</p>
</div>
);
export default Card;
In this example, twin.macro allows you to apply Tailwind styles conditionally, like adding bg-yellow-100 when the isFeatured prop is true. This kind of flexibility is ideal for Sitecore projects, where you need to dynamically adjust styles based on real-time data or personalized user content.
Another gem in twin.macro is its ability to nest responsive styles under breakpoints like md: and lg:. It helps cut down on repetition and keeps your code looking neat and organized.
Example: Nesting Breakpoints (md:, lg:)
import tw from 'twin.macro';
type ProfileCardProps = {
name: string;
bio: string;
};
const ProfileCard = ({ name, bio }: ProfileCardProps) => (
<div
css={[
tw`p-4 bg-white shadow-md rounded-md`, // Base styles
tw`md:(p-6 text-lg) lg:(p-8 text-xl bg-gray-100)`, // Grouped responsive styles
]}
>
<h4>{name}</h4>
<p>{bio}</p>
</div>
);
export default ProfileCard;
What’s happening here? Instead of repeating md:p-6 md:text-lg and lg:p-8 lg:text-xl all over the place, you can group those under md:(...) and lg:(...). It keeps your code tidy and easier to manage, especially when building responsive layouts.
Now, let’s talk about keeping your code nice and neat. The Prettier Tailwind plugin is your best friend when it comes to automatically sorting and organizing your Tailwind utility classes. Whether you’re using Tailwind CSS directly or styling with twin.macro, the plugin will make sure all your utility classes are ordered properly, every time you save.
Before and After Prettier Example:
// before
<div css={[tw`bg-gray-100 text-lg flex p-6 flex-col md:p-8 lg:shadow-lg`]}>
{/* Content */}
</div>
// after
<div css={[tw`flex flex-col p-6 text-lg bg-gray-100 md:p-8 lg:shadow-lg`]}>
{/* Content */}
</div>
It’s simple, but a game-changer for keeping your code clean. The plugin groups related utilities together (like flex and p-6), making everything easier to read and reducing those head-scratching moments when you’re trying to figure out why a class isn’t applying as expected.
When you’re working on a Next.js + Sitecore project with TypeScript, it all comes down to how dynamic your project needs to be. If you’re building a mostly static site, Tailwind CSS is a no-brainer—it’s fast, clean, and easy to use.
On the other hand, if you’re dealing with more complex, dynamic layouts or personalized data (which is a big deal in Sitecore), twin.macro gives you the flexibility you need to keep things manageable and maintainable over the long term. Add in Prettier to keep your utility classes in check, and you’ve got a recipe for a beautifully styled, highly dynamic project.