npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

prettier-plugin-tailwind-align

v0.2.0

Published

Prettier plugin that sorts Tailwind CSS classes and aligns object properties — in one pass.

Readme

prettier-plugin-tailwind-align

One Prettier plugin. Two formatters. Zero conflicts.

Sorts Tailwind CSS utility classes and vertically aligns object properties — in a single pass.

npm version npm downloads license node


Table of Contents


Why this exists

Prettier only allows one plugin to own a given parser hook (e.g. parsers["typescript"]). When prettier-plugin-tailwindcss and @huggingface/prettier-plugin-vertical-align are both listed in "plugins", whichever comes last silently wins the hook — breaking the other one with no error or warning.

The common workaround is a two-pass setup: run Tailwind sorting in one Prettier pass, alignment in another. This is fragile, slow, and ties your CI to a specific script order.

prettier-plugin-tailwind-align eliminates the problem entirely. It composes both behaviours internally so Prettier sees exactly one plugin, runs one pass, and both Tailwind class sorting and object alignment happen in the correct order — guaranteed.


Installation

# npm
npm install --save-dev prettier-plugin-tailwind-align

# pnpm
pnpm add -D prettier-plugin-tailwind-align

# yarn
yarn add -D prettier-plugin-tailwind-align

# bun
bun add --dev prettier-plugin-tailwind-align

Peer dependenciesprettier and prettier-plugin-tailwindcss must be installed separately (they are not bundled).

npm install --save-dev prettier prettier-plugin-tailwindcss

Quick start

Add the plugin to your .prettierrc (or prettier.config.js / prettier.config.ts):

{
  "plugins": ["prettier-plugin-tailwind-align"],
  "tailwindStylesheet": "./app/globals.css",
  "alignInGroups": "always"
}

That's it. Remove any separate prettier-plugin-tailwindcss or @huggingface/prettier-plugin-vertical-align entries — this plugin replaces both.


Options

All options from prettier-plugin-tailwindcss are fully supported and passed through unchanged — tailwindConfig, tailwindStylesheet, tailwindFunctions, tailwindAttributes, and so on.

In addition, this plugin adds the following options:

Complete .prettierrc example

{
  "plugins": ["prettier-plugin-tailwind-align"],
  "tailwindStylesheet": "./app/globals.css",

  "alignInGroups":           "always",
  "tailwindAlignAttributes": ["myClass", "triggerClass"],
  "tailwindAlignFunctions":  ["myMerge", "styled"],
  "tailwindAlignWidth":      0,
  "tailwindAlignGroups":     false,
  "tailwindAlignMinLength":  0,
  "tailwindAlignObjectValues": true
}

Option Reference

| Option | Type | Default | Description | |---|---|---|---| | alignInGroups | "never" \| "always" | "never" | Alignment scope inside object blocks | | tailwindAlignAttributes | string[] | [] | Extra JSX attribute names to treat as class strings | | tailwindAlignFunctions | string[] | [] | Extra function names whose arguments are class strings | | tailwindAlignWidth | number | 0 | Column width before wrapping; 0 = inherit printWidth | | tailwindAlignGroups | boolean | false | Group classes by Tailwind category (reserved, future feature) | | tailwindAlignMinLength | number | 0 | Min class string length to trigger formatting; 0 = always | | tailwindAlignObjectValues | boolean | true | Vertically align values in object class maps |


alignInGroups

Controls how alignment groups are computed inside object literals, interfaces, type literals, and class bodies.

| Value | Default | Description | |---|---|---| | "never" | ✅ | Every alignable property in the same block shares one alignment column. | | "always" | | A blank line starts a new independent group. Each group aligns to its own column. |

"never" — whole-block alignment

All properties align to the longest key in the entire block.

const palette = {
  red:       "#ef4444",
  green:     "#22c55e",
  blue:      "#3b82f6",
  darkGreen: "#166534",
};

"always" — group-level alignment

A blank line starts a new independent alignment group.

const palette = {
  red:   "#ef4444",
  green: "#22c55e",
  blue:  "#3b82f6",

  darkRed:   "#991b1b",
  darkGreen: "#166534",
  darkBlue:  "#1e3a8a",
};

tailwindAlignAttributes

string[] · default: []

Extra JSX attribute names (beyond the built-in list) that the plugin should treat as Tailwind class strings.

Built-in list already includes: className, class, wrapperClass, containerClass, buttonClass, labelClass, inputClass, and 20+ more common names.

{ "tailwindAlignAttributes": ["myClass", "triggerClass", "overlayClass"] }
// Before
<Modal myClass="  flex   flex-col  gap-4  " />

// After — myClass is now formatted like className
<Modal myClass="flex flex-col gap-4" />

tailwindAlignFunctions

string[] · default: []

Extra function names (beyond the built-in list) whose string arguments are treated as Tailwind class strings.

Built-in list: cn, cx, clsx, classnames, classNames, twMerge, twJoin, cva.

{ "tailwindAlignFunctions": ["myMerge", "styled", "composeStyles"] }
// Before
const cls = myMerge("  flex   items-center   gap-2  ", isActive && "bg-blue-500");

// After — myMerge arguments are now formatted
const cls = myMerge("flex items-center gap-2", isActive && "bg-blue-500");

tailwindAlignWidth

number · default: 0

Maximum column width before class strings are wrapped. 0 means inherit Prettier's own printWidth setting.

{ "tailwindAlignWidth": 60 }

tailwindAlignGroups

boolean · default: false

When true, classes will be grouped by Tailwind category (layout, visual, state, …) before rendering. Reserved for a future feature — currently has no effect.

{ "tailwindAlignGroups": false }

tailwindAlignMinLength

number · default: 0

Minimum character length of a class string before formatting is applied. Strings shorter than this value are left unchanged. 0 (default) means always format regardless of length.

Useful to leave short utility strings like "flex" or "hidden" alone:

{ "tailwindAlignMinLength": 20 }
// "flex" has 4 chars < 20 → left unchanged
const cls = "flex";

// "flex items-center gap-4" has 24 chars ≥ 20 → formatted
const layout = "flex items-center gap-4";

tailwindAlignObjectValues

boolean · default: true

When true (the default), values in object class maps are vertically aligned. Set to false to disable alignment while keeping class-string formatting.

{ "tailwindAlignObjectValues": false }
// tailwindAlignObjectValues: true (default)
const variants = {
  base:    "flex items-center",
  active:  "bg-blue-500 text-white",
  danger:  "bg-red-500 text-white",
};

// tailwindAlignObjectValues: false
const variants = {
  base: "flex items-center",
  active: "bg-blue-500 text-white",
  danger: "bg-red-500 text-white",
};

What gets aligned

| Node type | Aligned | |---|---| | Object literal properties — { key: value } | ✅ | | TypeScript interface members | ✅ | | TypeScript type literal members | ✅ | | Class property definitions | ✅ | | Shorthand properties — { x, y, z } | ❌ skipped | | Method shorthands — { foo() {} } | ❌ skipped | | Multi-line values (key and value on different lines) | ❌ skipped | | Multiple properties sharing the same line | ❌ skipped |


Full example

Input

const config = {
  host: "localhost",
  port: 3000,
  databaseName: "mydb",
  ssl: false,
};

const theme = {
  primary: "#3b82f6",
  secondary: "#6366f1",
  background: "#ffffff",
  text: "#111827",
  border: "#e5e7eb",
};

interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
  isAdmin: boolean;
}

type Dimensions = {
  width: number;
  height: number;
  depth: number;
};

const palette = {
  red: "#ef4444",
  green: "#22c55e",
  blue: "#3b82f6",

  darkRed: "#991b1b",
  darkGreen: "#166534",
  darkBlue: "#1e3a8a",
};

export function Card({ title, body }: { title: string; body: string }) {
  return (
    <div className="p-4 text-sm font-medium bg-white rounded-lg shadow-md flex items-center gap-2">
      <h2 className="text-xl leading-tight font-bold text-gray-900">{title}</h2>
      <p className="mt-2 text-base text-gray-600 leading-relaxed">{body}</p>
    </div>
  );
}

const buttonVariants = {
  base: "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
  primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500",
  secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-2 focus:ring-gray-400",
  danger: "bg-red-600 text-white hover:bg-red-700 focus:ring-2 focus:ring-red-500",
};

class ApiClient {
  baseUrl: string = "https://api.example.com";
  timeout: number = 5000;
  retries: number = 3;
  apiKey: string = "";
}

Output — with alignInGroups: "always"

const config = {
  host:         "localhost",
  port:         3000,
  databaseName: "mydb",
  ssl:          false,
};

const theme = {
  primary:    "#3b82f6",
  secondary:  "#6366f1",
  background: "#ffffff",
  text:       "#111827",
  border:     "#e5e7eb",
};

interface User {
  id:        number;
  name:      string;
  email:     string;
  createdAt: Date;
  isAdmin:   boolean;
}

type Dimensions = {
  width:  number;
  height: number;
  depth:  number;
};

const palette = {
  red:   "#ef4444",
  green: "#22c55e",   // group 1 — aligns to "green:" width
  blue:  "#3b82f6",

  darkRed:   "#991b1b",
  darkGreen: "#166534",  // group 2 — aligns to "darkGreen:" width independently
  darkBlue:  "#1e3a8a",
};

// Tailwind classes sorted, object values aligned — in one pass
export function Card({ title, body }: { title: string; body: string }) {
  return (
    <div className="flex items-center gap-2 rounded-lg bg-white p-4 text-sm font-medium shadow-md">
      <h2 className="text-xl leading-tight font-bold text-gray-900">{title}</h2>
      <p className="mt-2 text-base leading-relaxed text-gray-600">{body}</p>
    </div>
  );
}

const buttonVariants = {
  base:      "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
  primary:   "bg-blue-600 text-white hover:bg-blue-700 focus:ring-2 focus:ring-blue-500",
  secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-2 focus:ring-gray-400",
  danger:    "bg-red-600 text-white hover:bg-red-700 focus:ring-2 focus:ring-red-500",
};

class ApiClient {
  baseUrl: string = "https://api.example.com";
  timeout: number = 5000;
  retries: number = 3;
  apiKey:  string = "";
}

Migrating from two separate plugins

Before:

{
  "plugins": [
    "prettier-plugin-tailwindcss",
    "@huggingface/prettier-plugin-vertical-align"
  ],
  "tailwindStylesheet": "./app/globals.css",
  "alignInGroups": "always"
}

After:

{
  "plugins": ["prettier-plugin-tailwind-align"],
  "tailwindStylesheet": "./app/globals.css",
  "alignInGroups": "always"
}

Then uninstall the old plugins:

npm uninstall prettier-plugin-tailwindcss @huggingface/prettier-plugin-vertical-align

The alignInGroups option name is kept identical to @huggingface/prettier-plugin-vertical-align's option, so existing configs work without any value changes.


Local development

git clone https://github.com/w3Scribe/prettier-plugin-tailwind-align.git
cd prettier-plugin-tailwind-align
bun install

bun run build       # compile src/ → dist/
bun run dev         # watch mode
bun run typecheck   # type-check without emitting
bun run test        # run the test suite

Compatibility

| | Version | |---|---| | prettier | ^3.0 | | prettier-plugin-tailwindcss | ^0.7 | | Node.js | >=18 |


License

MIT © Sudhir Gadpayle