no-inline-styles

September 14, 2026 ยท View on GitHub

Use classes for styling. Pass dynamic values through CSS custom properties when a class needs them. This rule reports ordinary inline properties, hardcoded colors in custom properties, unreadable style objects, and <style> elements.

Setup

"shadcn/no-inline-styles": "error"

Keep this rule enabled inside your component directory too. It checks all JSX elements and does not need component import settings.

Examples

Classes and custom properties

// Allowed.
<div className="text-primary">Account settings</div>
<div className="text-(--label-color)" style={{ "--label-color": "var(--color-primary)" }} />

// Reported: an ordinary inline property.
<div style={{ color: "var(--color-primary)" }}>Account settings</div>

// Reported: a hardcoded color in a custom property.
<div style={{ "--label-color": "#ec4899" }}>Account settings</div>

The rule reports each disallowed property separately. A custom property can carry a dynamic value:

import * as React from "react"

export function Panel({ width }: { width: number }) {
  return (
    <div
      className="w-(--panel-width)"
      style={{ "--panel-width": `${width}px` } as React.CSSProperties}
    />
  )
}

Custom properties are checked for colors, not every kind of hardcoded value. Hex colors, named colors, color functions, and colors in gradients or shadows are reported. Theme variable references pass.

Variables and style objects

The rule reads same-file objects and lookup values one hop deep:

const colors = { accent: "#ec4899" }

// Reported: the lookup contains a raw color.
<div style={{ "--label-color": colors.accent }} />

An imported object, unknown function call, unreadable spread, or mutated object is reported as a dynamic style value:

import { panelStyle } from "./styles"

// Reported: the object cannot be checked here.
;<div style={panelStyle} />

Forwarded style props

A component can forward the style prop it received:

import * as React from "react"

export function Panel({ style, ...props }: React.ComponentProps<"div">) {
  return <div style={style} {...props} />
}

A local object named style is still checked. Defaults for the received prop are also checked because they are defined in the current file.

Style elements

A <style> element is reported, including CSS-in-JSX blocks:

// Reported.
<style>{".panel { color: red; }"}</style>

Class and property exceptions do not exempt <style> elements or unreadable style objects.

Allow an exception

For a property controlled by an animation library:

"shadcn/no-inline-styles": ["error", { allow: ["transform"] }]
// Allowed.
<div style={{ transform: "translateX(10px)" }} />

// Reported.
<div style={{ color: "red" }} />

Use CSS property names, not Tailwind classes. backgroundColor and background-color match the same property. border-* matches a family; --* matches every custom property.

An allowed property is not checked further, including for hardcoded colors. Use narrow exceptions when possible.

Contracts

A contract can allow a property on one component:

"shadcn/no-inline-styles": ["error", {
  contracts: [
    { pattern: "^Motion$", allow: ["transform"] },
  ],
}]
// Allowed by the contract.
<Motion style={{ transform: "translateX(10px)" }} />

// Reported: the contract does not apply to div.
<div style={{ transform: "translateX(10px)" }} />

This rule matches the JSX component name as written. It does not resolve wrappers to their underlying component. Lowercase elements use the top-level policy. See contract inheritance.

Your own words

"shadcn/no-inline-styles": ["error", {
  message: 'Use a class instead of {{property|inline CSS}}.',
}]

For style={{ color: "red" }}:

Use a class instead of color.

{{property}} uses the source spelling, such as backgroundColor. It is empty for dynamic style objects and <style> elements, so provide a fallback. {{component}} is empty on lowercase elements.

Options

OptionDefaultWhat it does
allowNot setExempts CSS properties from inline-style checks.
denyNot setRemoves exemptions. Without allow, exempts every other property.
contracts[]Sets property exceptions and messages for matching components.
messageBuilt-in guidanceReplaces error text.

A deny list can also report custom properties that would otherwise pass. Invalid entries such as bg-red-500 produce a configuration error. This rule has no recognition options.

Limits

  • Imported style values are reported as unreadable rather than inspected.
  • Forwarding a received style prop is allowed. Its authored defaults and any local properties added alongside it are checked.
  • A Button contract does not apply to a wrapper named SaveButton.

See analysis limits for shared limits.