Fields
Slug Field
The slug field renders a URL-safe identifier input in the admin UI and stores its value as a string in Convex. It can auto-generate a slug from another field, such as a title, converting the source value into a lowercase, hyphenated string. Use it for URL paths, unique identifiers, and any value that needs to be URL-safe. The underlying Convex validator is v.string(), wrapped in v.optional() when the field is not required.
Slug fields default to unique: true, which automatically adds a by_slug Convex index and enforces uniqueness on every create and update mutation. You can opt out by passing unique: false.
Config options
| Option | Type | Default | Description |
|---|---|---|---|
required | boolean | false | Makes the field required in both the schema and the admin UI. |
sourceField | string | — | Name of the sibling field to auto-generate the slug from. |
unique | boolean | true | Enforce uniqueness. Adds a by_slug Convex index and rejects create/update when another document has the same slug. Set to false to disable. |
indexed | boolean | — | Identical to unique. |
placeholder | string | — | Placeholder text shown when the input is empty. |
readOnly | boolean | false | Renders the input as disabled. |
label | string | (data) => string | Auto-generated | Custom label for the admin UI. |
description | string | — | Help text displayed below the label. |
condition | FieldCondition | — | Conditionally show or hide this field. |
sidebar | boolean | false | Place this field in the document sidebar. |
listColumn | boolean | false | Show as a default column in list views. |
searchable | boolean | false | Index this field for full-text search. |
Example usage
import { f, defineVextroCollection } from "vextro";
export const pages = defineVextroCollection({
slug: "pages",
label: "Pages",
collectionType: "content",
tableName: "pages",
fields: {
title: f.text({ required: true }),
slug: f.slug({
required: true,
sourceField: "title",
description: "URL path segment. Auto-generated from title.",
listColumn: true,
}),
},
}); Admin behavior
When sourceField is set, the slug field watches the specified sibling field and auto-generates a slug value as the user types. The generated slug is lowercase, with spaces and special characters replaced by hyphens. The user can override the auto-generated value by editing the slug field directly; once manually edited, auto-generation pauses until the field is cleared.
The slug input displays the value in a monospace font to reinforce that it represents a URL path segment.
Auto-generation example
export const blogPosts = defineVextroCollection({
slug: "blog-posts",
label: "Blog Posts",
collectionType: "content",
tableName: "blog_posts",
fields: {
title: f.text({ required: true }),
slug: f.slug({
required: true,
sourceField: "title",
}),
// Typing "My First Post" into title auto-fills slug with "my-first-post"
},
}); Manual slug
When no sourceField is set, the slug field behaves as a plain text input that accepts only URL-safe characters:
categorySlug: f.slug({
required: true,
placeholder: "e.g. winter-sale",
}), Disabling uniqueness
To create a slug field without uniqueness enforcement (no index, no duplicate check):
tag: f.slug({ unique: false }), Unique enforcement on other fields
The unique and indexed options are available on all field types, not just slugs. Any field marked unique: true or indexed: true gets a by_fieldName Convex index and uniqueness enforcement:
fields: {
email: f.text({ unique: true, required: true }),
sku: f.text({ indexed: true }),
slug: f.slug({ sourceField: "title" }), // unique by default
},