Plugin System Overview
PikaCSS has a powerful plugin system that lets you extend the engine's behavior at every stage — from config resolution to style generation. Plugins are plain objects created with the defineEnginePlugin() helper.
EnginePlugin Interface
Every plugin must have a name and can optionally define an order and hook functions:
import type { AtomicStyle, Engine, EngineConfig, ResolvedEngineConfig, ResolvedStyleDefinition, ResolvedStyleItem } from '@pikacss/core'
// This is a simplified view of the EnginePlugin interface.
// See packages/core/src/internal/plugin.ts for the full definition.
export interface EnginePlugin {
/** Unique plugin name (required) */
name: string
/** Execution order: 'pre' (0) → default (1) → 'post' (2) */
order?: 'pre' | 'post'
// --- Async hooks (can return modified payload) ---
/** Modify the raw config before it is resolved */
configureRawConfig?: (config: EngineConfig) => EngineConfig | void | Promise<EngineConfig | void>
/** Modify the resolved config */
configureResolvedConfig?: (resolvedConfig: ResolvedEngineConfig) => ResolvedEngineConfig | void | Promise<ResolvedEngineConfig | void>
/** Modify the engine instance after creation */
configureEngine?: (engine: Engine) => Engine | void | Promise<Engine | void>
/** Transform selectors during style extraction */
transformSelectors?: (selectors: string[]) => string[] | void | Promise<string[] | void>
/** Transform style items during engine.use() */
transformStyleItems?: (styleItems: ResolvedStyleItem[]) => ResolvedStyleItem[] | void | Promise<ResolvedStyleItem[] | void>
/** Transform style definitions during style extraction */
transformStyleDefinitions?: (styleDefinitions: ResolvedStyleDefinition[]) => ResolvedStyleDefinition[] | void | Promise<ResolvedStyleDefinition[] | void>
// --- Sync hooks (notification only) ---
/** Called after the raw config is settled */
rawConfigConfigured?: (config: EngineConfig) => void
/** Called when preflight CSS changes */
preflightUpdated?: () => void
/** Called when a new atomic style is generated */
atomicStyleAdded?: (atomicStyle: AtomicStyle) => void
/** Called when autocomplete configuration changes */
autocompleteConfigUpdated?: () => void
}Minimal Plugin
The simplest possible plugin only needs a name:
import { defineEnginePlugin } from '@pikacss/core'
export const myPlugin = defineEnginePlugin({
name: 'my-plugin',
})Plugin Ordering
The order property controls when a plugin's hooks execute relative to other plugins:
order value | Priority | Runs |
|---|---|---|
'pre' | 0 | First |
undefined (default) | 1 | Normal |
'post' | 2 | Last |
Within the same priority group, plugins run in the order they are registered. PikaCSS's built-in core plugins (e.g. core:variables, core:keyframes, core:selectors, core:shortcuts, core:important) are loaded before user plugins, then all plugins are sorted together.
import { defineEnginePlugin } from '@pikacss/core'
// Runs first (order: 'pre' → priority 0)
export const earlyPlugin = defineEnginePlugin({
name: 'early-plugin',
order: 'pre',
configureRawConfig(config) {
// Runs before default and post plugins
return config
},
})
// Runs second (order: undefined → priority 1)
export const normalPlugin = defineEnginePlugin({
name: 'normal-plugin',
// order is omitted — defaults to normal priority
configureRawConfig(config) {
return config
},
})
// Runs last (order: 'post' → priority 2)
export const latePlugin = defineEnginePlugin({
name: 'late-plugin',
order: 'post',
configureRawConfig(config) {
// Runs after pre and default plugins
return config
},
})Hook Lifecycle
During createEngine(config), hooks are invoked in this order:
createEngine(config)
│
├─ 1. configureRawConfig (async) — Modify the raw config
├─ 2. rawConfigConfigured (sync) — Notification: raw config settled
├─ 3. configureResolvedConfig (async) — Modify the resolved config
├─ 4. configureEngine (async) — Modify/set up the engine instance
│
└─ Engine is ready
│
├─ During engine.use(...):
│ ├─ 5. transformStyleItems (async) — Transform style items
│ ├─ 6. transformSelectors (async) — Transform selectors
│ └─ 7. transformStyleDefinitions (async) — Transform style definitions
│
├─ When preflights change:
│ └─ 8. preflightUpdated (sync) — Notification
│
├─ When atomic style is generated:
│ └─ 9. atomicStyleAdded (sync) — Notification
│
└─ When autocomplete config changes:
└─ 10. autocompleteConfigUpdated (sync) — NotificationHooks 1–4 run once during engine creation. Hooks 5–10 run at runtime whenever the corresponding event occurs.
Async Hooks (Transform)
Async hooks receive a payload and can return a modified version. The modified payload is then passed to the next plugin in order. If a hook returns void or undefined, the current payload is kept unchanged.
import { defineEnginePlugin } from '@pikacss/core'
export const asyncHookPlugin = defineEnginePlugin({
name: 'async-hook-example',
// Async hook: modify the raw config before it is resolved.
// Return the modified config to pass it to the next plugin,
// or return void/undefined to keep the current value.
configureRawConfig(config) {
config.prefix = 'pk-'
return config
},
// Async hook: transform selectors during style extraction.
// The returned array replaces the input for the next plugin.
transformSelectors(selectors) {
return selectors.map(s => s.replace('$hover', '&:hover'))
},
})configureRawConfig
- When: During
createEngine(), before config resolution - Receives:
config: EngineConfig— the raw user config - Returns:
EngineConfig | void - Purpose: Add plugins, modify prefix, add preflights, or set any config option before resolution
configureResolvedConfig
- When: During
createEngine(), after config resolution - Receives:
resolvedConfig: ResolvedEngineConfig— the fully resolved config - Returns:
ResolvedEngineConfig | void - Purpose: Modify resolved values like the autocomplete config or preflights list
configureEngine
- When: During
createEngine(), after the engine instance is created - Receives:
engine: Engine— the engine instance - Returns:
Engine | void - Purpose: Set up runtime features, add preflights, configure autocomplete, or attach custom properties to the engine
transformSelectors
- When: During style extraction (triggered by
engine.use()and preflight rendering) - Receives:
selectors: string[]— the selector chain being processed - Returns:
string[] | void - Purpose: Rewrite, expand, or replace selectors (e.g. mapping
$hoverto&:hover)
transformStyleItems
- When: During
engine.use(), before style items are resolved - Receives:
styleItems: ResolvedStyleItem[]— the list of style items - Returns:
ResolvedStyleItem[] | void - Purpose: Add, remove, or transform style items (e.g. expanding shortcuts)
transformStyleDefinitions
- When: During style extraction, when processing nested style definitions
- Receives:
styleDefinitions: ResolvedStyleDefinition[]— the list of style definitions - Returns:
ResolvedStyleDefinition[] | void - Purpose: Modify style definition objects before they are extracted into atomic styles
Sync Hooks (Notification)
Sync hooks are notification-only — they inform plugins that something happened. They should not return a value.
import { defineEnginePlugin } from '@pikacss/core'
export const syncHookPlugin = defineEnginePlugin({
name: 'sync-hook-example',
// Sync notification hook: called after the raw config is settled.
// Use it to read the config — do NOT return a value.
rawConfigConfigured(config) {
console.log('Config settled with prefix:', config.prefix)
},
// Sync notification hook: called when a new atomic style is added.
// Useful for tracking, logging, or collecting generated styles.
atomicStyleAdded(atomicStyle) {
console.log('New atomic style:', atomicStyle.id)
},
// Sync notification hook: called when preflight CSS changes.
preflightUpdated() {
console.log('Preflight updated')
},
// Sync notification hook: called when autocomplete config changes.
autocompleteConfigUpdated() {
console.log('Autocomplete config updated')
},
})rawConfigConfigured
- When: During
createEngine(), afterconfigureRawConfigcompletes - Receives:
config: EngineConfig— the settled raw config - Purpose: Read the final raw config (e.g. to cache values for later use in other hooks)
preflightUpdated
- When: Whenever a preflight is added or modified
- Receives: nothing
- Purpose: React to preflight changes (e.g. trigger a rebuild)
atomicStyleAdded
- When: Whenever a new atomic style is generated and stored
- Receives:
atomicStyle: AtomicStyle—{ id: string, content: StyleContent } - Purpose: Track or log generated atomic styles
autocompleteConfigUpdated
- When: Whenever the autocomplete configuration changes
- Receives: nothing
- Purpose: React to autocomplete changes (e.g. rebuild completion data)
Hook Execution Model
All hooks — both async and sync — follow the same execution rules:
- Plugin order: Hooks run plugin-by-plugin in sorted order (
pre→ default →post) - Payload chaining: For async hooks, if a plugin returns a non-nullish value, that value replaces the payload for the next plugin
- Error isolation: If a plugin's hook throws an error, the error is caught and logged. Execution continues with the next plugin — one failing plugin does not break the chain
- Skipping: If a plugin does not define a particular hook, it is simply skipped
Complete Example
A full plugin using all available hooks:
import { defineEnginePlugin } from '@pikacss/core'
export function createMyPlugin(options: { prefix?: string } = {}) {
const { prefix = 'my' } = options
return defineEnginePlugin({
name: `${prefix}-plugin`,
order: 'pre',
// --- Async hooks (transform) ---
configureRawConfig(config) {
// Modify raw config before resolution
config.prefix = config.prefix || prefix
return config
},
configureResolvedConfig(resolvedConfig) {
// Modify resolved config after resolution
return resolvedConfig
},
async configureEngine(engine) {
// Set up engine features, add preflights, etc.
engine.addPreflight('/* my-plugin preflight */')
engine.appendAutocompleteExtraProperties('__myProp')
},
transformSelectors(selectors) {
// Transform selectors during style extraction
return selectors
},
transformStyleItems(styleItems) {
// Transform style items during engine.use()
return styleItems
},
transformStyleDefinitions(styleDefinitions) {
// Transform style definitions during style extraction
return styleDefinitions
},
// --- Sync hooks (notification) ---
rawConfigConfigured(_config) {
// Read-only access to settled raw config
},
preflightUpdated() {
// React to preflight changes
},
atomicStyleAdded(_atomicStyle) {
// React to new atomic styles
},
autocompleteConfigUpdated() {
// React to autocomplete config changes
},
})
}Source Reference
packages/core/src/internal/plugin.ts—EnginePlugininterface,defineEnginePlugin, hook execution, plugin sortingpackages/core/src/internal/engine.ts—createEngine, hook invocation during engine lifecycle
Next
- Continue to Create Plugin for a step-by-step guide to building your own plugin