The Power of @apply: When and How to Extract Custom Classes

Learn how to simplify your Tailwind CSS workflow with the @apply directive for cleaner code and reusable styles.

Web Development
Mar 1, 2025
The Power of @apply: When and How to Extract Custom Classes

Want cleaner code and reusable styles in Tailwind CSS? The @apply directive helps you group utility classes into custom CSS classes, making your HTML more readable and your code easier to maintain. Here’s what you need to know:

  • What is @apply? It lets you combine Tailwind utility classes into reusable custom classes in your CSS.
  • Why use it? Simplify repeated styles, improve consistency, and make updates easier.
  • When to use it? For repeated patterns, complex components, or shared design styles.

Quick Benefits of @apply:

  • Cleaner HTML: Shorter, more readable class lists.
  • Reusable Styles: Centralize common patterns into one class.
  • Responsive & State Variants: Works seamlessly with Tailwind’s breakpoints and states.
  • Performance Boost: Helps with smaller CSS files via tree-shaking.

Ready to streamline your Tailwind workflow? Let’s break it down step-by-step.

Best Times to Use Custom Classes

Using custom classes can make your code cleaner and more efficient. They’re particularly helpful when specific patterns or styles are reused across your project. The key is knowing when to extract utility classes into custom ones.

Spotting Repeated Utility Patterns

Take a close look at your codebase for utility combinations that pop up often. Tools like Tailwind CSS IntelliSense can make this process easier. For instance, if you find a repeated pattern like this:

<div class="bg-white shadow-lg rounded-lg p-6 m-4 hover:shadow-xl">

It’s a good idea to create a custom class for it:

.card {
  @apply bg-white shadow-lg rounded-lg p-6 m-4 hover:shadow-xl;
}

By consolidating repeated styles into a single class, you simplify your code and reduce duplication.

Custom Classes vs. Utility Classes

Choosing between custom and utility classes depends on what you’re working on. Here’s a quick comparison:

ScenarioBest ChoiceWhy?
Unique stylesUtility classesIdeal for one-off designs
Complex componentsCustom classesMakes code easier to read and manage
Shared design patternsCustom classesKeeps styles consistent
Quick prototypingUtility classesSpeeds up experimentation

Think about whether your project needs highly reusable design tokens or more specific component classes.

Component Classes vs. Design Token Classes

Decide whether to use component-specific classes or design token classes based on your project’s structure. Here’s how they differ:

  • Component-specific classes: Great for unique elements in your UI.
  • Design token classes: Act as reusable building blocks for your design system.

Example of a component-specific class:

.product-card {
  @apply bg-white p-4 shadow-md rounded-lg;
}

Example of a design token class:

.primary-button {
  @apply bg-blue-500 text-white font-bold py-2 px-4 rounded hover:bg-blue-700;
}

Step-by-Step Guide to Using @apply

Follow these steps to use @apply effectively and keep your styles organized and easy to maintain.

Setting Up Custom Layers

Define custom style layers in your tailwind.config.js file to organize your styles:

module.exports = {
    // ...other config
    layers: {
      components: true,
      utilities: true,
    },
}

These layers help prioritize styles:

LayerPurposeExamples
BaseFoundational stylesTypography, resets
ComponentsReusable design blocksButtons, cards, forms
UtilitiesSingle-use classesCustom utility classes

Creating and Using Custom Classes

Bundle common utility classes into reusable custom classes:

@layer components {
    .btn-primary {
      @apply py-2 px-4 bg-blue-500 text-white font-semibold rounded-lg;
      @apply hover:bg-blue-700 focus:ring-2 focus:ring-blue-400;
    }
}

Store these styles in separate files for better organization. This approach ensures modularity and makes your code easier to manage.

Adding Variants and Breakpoints

Use responsive prefixes and group related styles to handle different screen sizes and states:

.responsive-card {
    @apply p-4 bg-white shadow-md;
    @apply sm:p-6 sm:rounded-lg;
    @apply md:p-8 md:shadow-lg;
    @apply lg:max-w-3xl lg:mx-auto;
}

For components with multiple states, structure your styles logically:

.interactive-element {
    @apply bg-gray-100 text-gray-800;
    @apply hover:bg-gray-200 hover:text-gray-900;
    @apply focus:outline-none focus:ring-2;
    @apply dark:bg-gray-800 dark:text-gray-200;
}

Advanced @apply Methods

Taking custom classes a step further, these advanced techniques can make your CSS even more efficient and easier to manage.

State-Based Styling

You can use @apply to define styles for different states:

/* Button with interactive states */
.interactive-button {
  @apply bg-blue-500 text-white px-4 py-2 rounded transition-colors;
  @apply hover:bg-blue-600 active:bg-blue-700;
  @apply focus:outline-none focus:ring-2 focus:ring-blue-300;
  @apply disabled:bg-gray-400 disabled:cursor-not-allowed;
}

/* Basic form input styling */
.form-input {
  @apply border-2 rounded px-3 py-2 w-full;
  @apply focus:border-blue-500 focus:ring-1 focus:ring-blue-200;
}

For form validation states, you can create specific styles like this:

.input-field {
  @apply border-2 rounded-md p-2;
}

.input-success {
  @apply border-green-500 bg-green-50 text-green-700;
  @apply focus:border-green-600 focus:ring-green-200;
}

.input-error {
  @apply border-red-500 bg-red-50 text-red-700;
  @apply focus:border-red-600 focus:ring-red-200;
}

Once you’ve mastered state-based styling, you can extend these ideas to themes and dark mode.

Theme and Dark Mode Setup

Switching themes becomes simple with CSS custom properties and @apply:

:root {
  --bg-primary: theme('colors.white');
  --text-primary: theme('colors.gray.900');
}

.dark {
  --bg-primary: theme('colors.gray.900');
  --text-primary: theme('colors.gray.100');
}

.themed-container {
  @apply bg-[var(--bg-primary)] text-[var(--text-primary)];
  @apply transition-colors duration-200;
}

For example, Vercel’s documentation team reported a 32% reduction in CSS bundle size and a 250ms improvement in First Contentful Paint on their documentation pages after implementing this method in September 2023 [1].

To ensure smooth performance, it’s crucial to optimize your @apply usage.

Optimizing @apply Performance

Here are some strategies to maintain performance while using @apply:

StrategyHow to ImplementBenefits
Layer OrganizationUse @layer for custom stylesSimplifies specificity management
Code SplittingSeparate theme-specific stylesShrinks the initial bundle size
JIT ModeEnable Tailwind’s JIT compilerGenerates styles on demand

You can also group related styles into component classes for better performance:

@layer components {
  .optimized-card {
    @apply bg-white rounded-lg shadow-md p-4;
    @apply dark:bg-gray-800 dark:shadow-none;
    @apply hover:shadow-lg transition-shadow;
  }
}

Lastly, enabling Tailwind’s JIT mode ensures that only the styles you need are generated, keeping your CSS lightweight and fast.

Making the Most of @apply

The @apply directive in Tailwind CSS is a handy feature for simplifying and organizing your web development workflow. It allows you to streamline repetitive utility classes into custom CSS, making your code easier to manage while keeping the benefits of utility-first design.

Key Takeaways

  • Group repeated utility classes into custom classes to reduce clutter.
  • Use @apply for dynamic components that require state-based styling.
  • Combine responsive utilities with custom classes to ensure consistency across different screen sizes.

For more complex components, you can pair @apply with state management. Here’s an example:

.interactive-component {
    @apply bg-white rounded-lg shadow-md transition-all;
    @apply hover:shadow-lg focus:ring-2 focus:ring-blue-500;
}

This approach keeps your styles clean and efficient while maintaining flexibility for dynamic and responsive designs.

FAQs

Here are answers to some common questions to clarify key points covered earlier.

What is tailwind @apply?

The @apply directive in Tailwind CSS allows you to group multiple utility classes into a single reusable class. This makes it easier to maintain consistent styles while keeping the utility-first flexibility that Tailwind offers.

How do you use @apply in CSS?

Here’s how you can use the @apply directive:

  1. Create a CSS class: Define a custom class in your CSS file.
  2. Use utility classes with @apply: Add the desired Tailwind utility classes inside the @apply directive.
  3. Use the custom class in your HTML: Apply the new class to your HTML elements.

Example:

.custom-button {
    @apply bg-blue-500 text-white font-bold py-2 px-4 rounded;
}

Why use the @apply directive in Tailwind?

The @apply directive helps you combine the best of utility-first CSS and traditional component-based styling by:

  • Simplifying repetitive style patterns
  • Keeping your HTML cleaner by reducing long class lists
  • Ensuring consistent styling across your project
  • Making your code easier to manage and update

Does @apply work with responsive and state variants?

Yes, @apply works perfectly with Tailwind’s responsive utilities and state variants, allowing you to build dynamic, responsive components while keeping your code organized.

FeatureAdvantage
Style AbstractionCuts down on repetitive utility classes
Responsive SupportIntegrates with breakpoint modifiers
Variant CompatibilitySupports hover, focus, and other states
Specificity ManagementHandles CSS specificity automatically

“The @apply directive removes important by default to avoid specificity issues, but you can add it back if needed.”

When using @apply, strike a balance between utility classes and custom components to fully leverage Tailwind’s features without overcomplicating your styles.

Share this post

Supercharge your web development workflow

Take your productivity to the next level, Today!

Written by
Author

Himanshu Mishra

Indie Maker and Founder @ UnveelWorks & Hoverify