PostCSS Built-in Node Types

PostCSS AST node types — Root, Rule, AtRule, Declaration, Comment — with API.

Reference for PostCSS CSS AST node types (Root, Rule, AtRule, Declaration, Comment) with their key properties, walk/replace methods and the transformation API used when writing PostCSS plugins. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is a PostCSS node?

PostCSS parses CSS into an Abstract Syntax Tree (AST) of nodes. Every node has a type property identifying it — root, atrule, rule, decl or comment. Plugins read and mutate these nodes to transform the stylesheet before it is stringified back to CSS.

The PostCSS node tree

PostCSS turns a stylesheet into a tree of typed nodes. A Root contains Rule and AtRule children; rules and at-rules contain Declaration and Comment nodes. Plugins walk this tree, read properties like selector, prop and value, and mutate or replace nodes — then PostCSS stringifies the tree back to CSS. This reference lists each built-in node type and the API you use to transform it.

How it works

Every node carries a type string and links to its parent. Containers (Root, Rule, AtRule) hold a nodes array of children and expose walk* helpers to traverse them:

module.exports = () => ({
  postcssPlugin: "demo",
  Once(root, { Declaration }) {
    root.walkDecls("color", (decl) => {
      if (decl.value === "red") decl.value = "#f00";
    });
    root.walkAtRules("media", (atRule) => {
      // atRule.name === "media", atRule.params === "(min-width: 768px)"
    });
  },
});
module.exports.postcss = true;

Walk callbacks run depth-first. Returning nothing continues; calling decl.remove() or rule.replaceWith(...) mutates the tree in place.

Node type quick reference

TypeIdentifiesKey properties
rootThe whole documentnodes array of children
ruleA selector blockselector string, nodes
atrule@ keywordname (e.g. media), params, nodes (undefined for statement at-rules)
declA property: value pairprop, value, important (boolean)
comment/* CSS comment */text (contents without delimiters)

Visitor API vs Once + walkDecls

PostCSS 8 introduced a visitor API where your plugin exports a plain object with method names matching node types:

// Visitor API — PostCSS batches a single pass over all plugins
module.exports = {
  postcssPlugin: "my-plugin",
  Declaration(decl) {
    if (decl.prop === "color" && decl.value === "red") {
      decl.value = "#f00";
    }
  },
};
module.exports.postcss = true;

The visitor API is preferred because PostCSS can combine multiple plugins into a single AST traversal, while Once + walkDecls forces a separate pass per plugin. For complex plugins that need to see the whole tree before acting, Once is still appropriate.

Common mutations

// Remove a declaration
decl.remove();

// Replace a rule with a comment
rule.replaceWith(postcss.comment({ text: "removed" }));

// Append a new declaration to a rule
rule.append({ prop: "display", value: "flex" });

// Clone before inserting elsewhere
const copy = decl.clone({ value: "new-value" });
rule.append(copy);

Tips and notes

  • Prefer the visitor API (Declaration(decl, helpers)) over Once + walk* for plugins that compose well — PostCSS batches a single AST pass.
  • Use node.clone() before reusing a node elsewhere; nodes can only have one parent.
  • decl.important is a boolean, not part of value; preserve it when rewriting.
  • atRule.nodes is undefined for statement at-rules like @import, and an array for block at-rules like @media.
  • Check node.source for the original position (file, line, column) when generating sourcemaps or producing useful error messages from your plugin.