Skip to main content

stacks-stx

Use when working with STX templates in a Stacks application - template syntax, components, directives, signals, reactivity, SSR, streaming, hydration, or debugging STX rendering. STX is the ONLY templating system for Stacks.

Source facts

Repository
stacksjs/wildloop
Last source activity
October 20, 2026 at 22:03
Detected SKILL.md language
English
Stars
2
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
stacks-stx
description
Use when working with STX templates in a Stacks application - template syntax, components, directives, signals, reactivity, SSR, streaming, hydration, or debugging STX rendering. STX is the ONLY templating system for Stacks.
license
MIT
compatibility
Bun >= 1.3.0, TypeScript
allowed-tools
Read Edit Write Bash Grep Glob
# STX Templating Engine STX is the full-stack templating and component framework for Stacks. It handles template rendering, reactivity, SSR, streaming, hydration, and more. ## Design & anti-slop skills When the task is how a page should *look* (not just how stx renders), pair this skill with the design-taste family: `stacks-design-taste` (flagship), the aesthetic presets `stacks-design-soft` / `stacks-design-minimalist` / `stacks-design-brutalist`, `stacks-redesign`, `stacks-design-output`, and the image-first `stacks-image-to-code` / `stacks-imagegen-web` / `stacks-imagegen-mobile` / `stacks-brandkit`. They translate premium design rules into stx + ts-css + composables. ## Key Paths - STX config: `config/ui.ts` - STX plugin: `bun-plugin-stx` (loaded via bunfig.toml) - STX build cache + route manifest: `storage/framework/stx/` (stx's `stateDir`, set in `config/ui.ts`) - Components: `resources/components/` - Layouts: `resources/layouts/` - Partials: `resources/partials/` - Views: `resources/views/` - Package: `@stacksjs/stx` - Utility CSS configuration: `config/css.ts` (`@stacksjs/ts-css/engine`) ## CRITICAL Rules 1. **ALWAYS use STX** for templating - never write vanilla JS 2. **NEVER use** `var`, `document.*`, `window.*` in STX templates 3. STX `<script>` tags should ONLY contain stx-compatible code (signals, composables, directives) ### Pre-paint appearance Use `@appearanceBootstrap({...})` when persisted appearance must be applied before the browser parses the application shell. Do not add a raw inline script for `localStorage`, `document`, or `matchMedia`. The directive emits the synchronous compiler-owned guard, validates storage through explicit allowlists, applies the root data attributes and `dark` class, and receives the request CSP nonce when nonce support is enabled. ```stx @appearanceBootstrap({ storageKey: 'app-appearance', appearance: { key: 'sidebarStyle', attribute: 'appearance', allowed: ['macos', 'arc'], default: 'macos', }, colorMode: { key: 'colorMode', attribute: 'color-mode', default: 'system', }, }) ``` Persist a JSON object under `storageKey`. Invalid JSON, storage errors, unknown appearance values, and unsupported color modes all fall back to the declared defaults before first paint. ## Template Structure ```html <script client> const count = state(0) const title = state('Hello STX') const showDescription = state(true) function increment(): void { count.update(value => value + 1) } </script> <template> <div class="container"> <h1>{{ title() }}</h1> <p :if="showDescription()">Rendered reactively</p> <button type="button" @click="increment()">Count: {{ count() }}</button> </div> </template> <style> /* Use ts-css utility classes or custom CSS */ .container { max-width: 1200px; margin: 0 auto; } </style> ``` STX signals are callable. Read with `count()`, replace with `count.set(value)`, and update from the current value with `count.update(value => value + 1)`. Do not use Vue-style `.value` access in STX templates or `resources/functions`. ### Client loop conditionals Attribute loops create a client-owned scope. Their item and index aliases may be used with either reactive attribute conditionals or STX block conditionals. The compiler promotes a block chain that reads those aliases into one reactive runtime chain: ```html <template :for="(field, index) in fields()"> @if(field.editable) <input x-model="drafts[field.key]"> @elseif(index === 0) <span>First read-only field</span> @else <span>{{ field.value }}</span> @endif </template> ``` Loop aliases are scoped to the owning element and inherited by nested client loops. A condition outside that element remains server-owned. Do not mix a server context value into a client loop chain. Serialize the value into client state when the browser must reevaluate it. ### Native form binding Use `x-model` for two-way form state instead of pairing `:value` with a manual `@input` or `@change` handler. The expression is the signal name without parentheses: ```html <script client> const query = state('') const quantity = state(1) const enabled = state(false) const categories = state<string[]>([]) </script> <input x-model.trim="query" type="search"> <input x-model.number="quantity" type="number"> <input x-model="enabled" type="checkbox"> <input x-model="categories" type="checkbox" value="news"> <select x-model="query"> <option value="">All</option> <option value="active">Active</option> </select> ``` `x-model` supports text inputs, textareas, selects, radio buttons, boolean checkboxes, and checkbox arrays. Use `.trim` when whitespace should be removed and `.number` when the signal should receive a number. Keep a dedicated signal for each binding. Arbitrary expressions such as `x-model="user.name"` are not writable bindings. ## Configuration (config/ui.ts) ```typescript import type { StxOptions } from '@stacksjs/stx' export default { componentsDir: 'components', layoutsDir: 'layouts', partialsDir: 'partials', } satisfies StxOptions ``` ### Configuration source Import `StxOptions` from the installed package and use `satisfies`; the exported type and `config/ui.ts` are authoritative. Optional rendering, hydration, streaming, SEO, i18n, component/plugin, form, PWA and strict settings vary with the installed STX version. Adding a key to a handwritten interface does not enable a module. Read the implementation and retained test for the selected setting instead of copying an obsolete flattened config shape. ## Request, state and component boundaries Use `config/ui.ts`'s explicit `root: 'resources'` and relative component, layout and partial directories. `stateDir: 'storage/framework/stx'` owns caches, client bundles and generated route artifacts. Keep application overrides under `resources/`; framework defaults remain the fallback. `defaultViews` controls which auth/storefront/error/mail views are served, and its default follows the mounted default route bundles. A view existing on disk does not establish that its POST endpoint is enabled. Server scripts run per render; client scripts run in the browser. Serialize server data through the supported hydration bridge rather than sharing mutable process state. In server templates read the request snapshot (including site) through `requestContext`; API AsyncLocalStorage site context does not extend through STX's asynchronous render boundary. Pair tenant features with `stacks-sites`. The browser has both eagerly delivered and demand-bundled composables. Observers, motion helpers and STX `useForm` are usable client primitives in current STX, even when they are not explicit `window.name` aliases. Read `stacks-composables/BROWSER.md` before choosing a signature or mixing the browser signal API with explicit module Refs. For account-persisted initial UI use `keptState` or `cachedQuery`, establish `setKeptScope` before values are created, and clear the account scope on sign-out. Use `clearServerData(key?)` after mutations invalidate hydration data. Ordinary localStorage is a different persistence boundary. ### Live API state and offline caching An enabled offline worker may answer API reads from its saved copy while it refreshes in the background. For live job progress, connection status and other reads whose stale answer could end a running operation, use `fetch(url, { cache: 'no-store' })` and return `Cache-Control: private, no-store` from the endpoint. STX >= 0.2.428 forwards no-store requests directly to the network without reading, writing or falling back to its offline cache; no-store responses also remove earlier saved copies. A failed network read must remain a failed read, rather than appearing to confirm that a background operation finished. Keep ordinary offline-capable calendar and workout reads on their configured cache strategy. ## STX capability discovery ### Core - Template parsing and compilation - Reactivity system (ref, computed, watch) - Component composition and lifecycle - Dependency injection (provide/inject) - DOM update scheduling with the browser-auto-imported `nextTick()` ### Template refs after structural updates Use `useRef()` with `nextTick()` when an element is inserted by `:if`, a modal, or another structural directive and must be focused or measured immediately afterward: ```ts const open = state(false) const searchInput = useRef('searchInput') function showSearch(): void { open.set(true) void nextTick(() => searchInput.current?.focus()) } ``` ```html <template :if="open()"> <input ref="searchInput" type="search"> </template> ``` `nextTick()` runs after the current synchronous signal and effect flush. Prefer it over `querySelector`, manual DOM polling, or `requestAnimationFrame` when the task is waiting for STX to materialize reactive markup. ### Rendering - **SSR** - Server-Side Rendering - **Streaming** - Progressive HTML streaming - **Hydration** - Progressive and islands-based hydration - **Suspense** - Async component loading with fallbacks - **Error boundaries** - Graceful error handling in components ### Features - **Router** - Client-side routing - **Forms** - Built-in form handling and validation - **i18n** - Internationalization support - **SEO** - Meta tags, Open Graph, Twitter cards, structured data - **PWA** - Progressive Web App support - **Animation** - CSS and JS animation system - **Markdown** - Markdown rendering with syntax highlighting - **A11y** - Accessibility checking and auto-fixing ### SEO directives Every argument is an expression evaluated against the template context (`<script server>` variables, the render context, and, inside an `@include` partial, the partial's own `<script server>`). A quoted string is literal text. ```html <script server> const seo = { title: post.title, description: post.excerpt, canonical: url, openGraph: { image: post.cover } } </script> @seo(seo) <!-- or @seo({ ...seo, title }), @seo(buildSeo(post)), @seo({ title: 'Literal' }) --> @seo(page.seo ?? {}) <!-- a variable only some pages define --> @meta('author', post.author) <!-- unquoted value: an expression --> @meta('description', 'Plain text') <!-- quoted value: literal, never looked up --> @meta('keywords', tags) <!-- an array joins with ", " --> @meta('og:title') <!-- one argument: reads `title`, then `openGraph.title` --> @metaTag({ httpEquiv: 'refresh', content: '30' }) @structuredData(product) <!-- object or array of objects; @context defaults to schema.org --> ``` - `@seo` reads only `title`, `description`, `keywords`, `robots`, `canonical`, `openGraph`, `twitter`, `structuredData`. `image`, `url` and `type` are not top-level keys (use `openGraph.image`, `canonical`, `openGraph.type`); stray keys are ignored with a warning. - A directive that cannot produce its tag (a variable that is not defined, a value of the wrong type, a syntax error, an unclosed call) leaves `<!-- [SEO Error ...] -->` in its place and logs `[stx] @seo(seo) in <file>: ...`. A defined variable holding `null` renders nothing, quietly. Read those warnings; they are the only sign a page shipped without its preview card. - `@meta` in a page is staged into `<head>`. `@seo`, `@metaTag` and `@structuredData` write where they sit, so put them inside `<head>` (a layout or head partial). In a page body use `useSeoMeta({ title, description, ogImage })` in `<script server>`, which always reaches `<head>`. - Requires `@stacksjs/stx` >= 0.2.343. Older versions only matched `@seo({ ... })` literally and sent unquoted `@meta` values such as `post.author` as text, without a warning. ### Dev Tools - **Dev server** with HMR (Hot Module Replacement) - **Image optimization** - **Asset pipeline** - **Testing utilities** ## Plugin Loading ```toml # bunfig.toml [serve] plugins = ["bun-plugin-stx"] ``` The STX plugin processes `.stx` files during serve and build. ## Scaffolding ```typescript import { createProject, addComponent, addPage, addStore, addLayout } from '@stacksjs/stx' // Create a new project await createProject('my-app', { template: 'dashboard' }) // Add to existing project await addComponent('UserCard', { props: true, styles: true }) await addPage('about', { layout: 'default' }) await addStore('cart', { persist: true, actions: true }) await addLayout('admin', { nav: true, footer: true }) ``` ### Project Templates `default`, `minimal`, `full`, `blog`, `dashboard`, `landing` ## Gotchas - **STX is the ONLY templating system** - do not use other template engines - **`bun-plugin-stx` must be loaded** - without it, `.stx` files won't be processed - **Client delivery** - eager runtime/compiler primitives, demand composables and explicit module imports are distinct. The ambient browser manifest does not establish which browser implementation or signature is delivered. - **Imported module dependencies** - browser auto-imports are injected into the STX script entry only; imported `.ts` modules must explicitly import every function or store they use - **Project-root server imports** - use `~/path` or `@/path` inside `<script server>` when a layout, partial, or page needs a project file. STX resolves both aliases against the application root before executing the server script. Relative imports still resolve against the `.stx` file - **Browser package inputs are bundled** - core browser helpers and model auto-import bootstraps are compiler bundle inputs. Rendered HTML must never contain a bare `import '@stacksjs/browser'`, because browsers cannot resolve package specifiers without an import map - **Server-to-client values are explicit** - a JSON-serializable top-level value exported by `<script server>` can be referenced by name in `<script client>`. STX serializes only referenced values and never overwrites a client-owned declaration - **`storage/framework/stx/`** - stx build cache and the generated route manifest. `config/ui.ts` sets stx's `stateDir` here, so nothing lands in the project root. Gitignored; safe to delete - **Reactivity is signal-based** - use callable `state()` and `derived()` signals, not Vue-style `ref()` or `.value` - **Client loop conditions** - `@if` chains that read `:for` or `@for` aliases compile into scoped runtime chains; aliases do not leak outside the loop element
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub