Skip to main content
These are the most common mistakes we see in Vertz projects — especially when AI agents or developers are getting started. Each one has a clear fix.

Skipping vtz create

The fastest and most reliable way to start a Vertz project is vtz create. It scaffolds a working full-stack app with the compiler, dev server, SSR, typed API client, and theme — all configured correctly.
Why it matters: Vertz requires specific compiler configuration, JSX import source settings, dev server setup, and file conventions. Manual setup is error-prone and misses features like SSR, HMR, scoped CSS extraction, and the typed API client. Start with vtz create and modify the scaffolded code.

Using signal() instead of let

The Vertz compiler transforms let declarations into signals automatically and auto-unwraps .value in JSX. Using signal() directly bypasses these optimizations.
Why it matters: When you use let, the compiler tracks dependencies automatically — queries re-fetch, DOM updates, and effects re-run without manual .value reads. With signal(), you must manage everything yourself.

Using () => true instead of rules.public

Entity access rules should use declarative rules.* descriptors, not callback functions.
Why it matters: rules.* descriptors are plain objects that the framework can serialize for the client SDK, display in generated OpenAPI docs, and potentially compile to database-level policies. Callback functions are opaque. See Entities for the full access control API.

Using raw fetch() instead of the generated SDK

Vertz auto-generates a typed SDK from your entity and service definitions. Always use it.
Why it matters: The generated SDK provides type safety (request and response types checked at compile time), SSR integration (queries run on the server during SSR), automatic cache invalidation (mutations refetch related queries), and optimistic updates. See Data Fetching for the full guide.

Manual refetch() instead of reactive queries

When queries read reactive state inside their thunk, they automatically re-fetch when that state changes. Manual refetch() calls are usually unnecessary.
See Data Fetching for how dependency tracking works.

Calling components as functions

Always use JSX syntax for components. Calling them as functions bypasses the compiler’s reactive prop generation.

Annotating component return types

Let TypeScript infer return types from JSX. Annotating : HTMLElement is a lossy upcast.

Running SQLite scripts with node instead of vtz

The vtz runtime includes a built-in SQLite driver powered by Rust — no extra dependencies needed. Running scripts that use @vertz/db with SQLite under plain node will fail because Node.js doesn’t have the native driver.
Why it matters: The vtz runtime embeds SQLite via Rust, so any script run through vtz (including vtz run <script>, vtz dev, and vtz test) has SQLite available with zero extra dependencies. Running the same script with node bypasses the runtime entirely. If you need to run database scripts outside of vtz, use PostgreSQL instead of SQLite, or install better-sqlite3 as a fallback.

Using .references() on columns for foreign keys

Foreign keys are declared via d.ref.one() on models, not .references() on columns.
See Schema for the full relations API.