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

OptionTypeDefaultDescription
requiredbooleanfalseMakes the field required in both the schema and the admin UI.
sourceFieldstring—Name of the sibling field to auto-generate the slug from.
uniquebooleantrueEnforce uniqueness. Adds a by_slug Convex index and rejects create/update when another document has the same slug. Set to false to disable.
indexedboolean—Identical to unique.
placeholderstring—Placeholder text shown when the input is empty.
readOnlybooleanfalseRenders the input as disabled.
labelstring | (data) => stringAuto-generatedCustom label for the admin UI.
descriptionstring—Help text displayed below the label.
conditionFieldCondition—Conditionally show or hide this field.
sidebarbooleanfalsePlace this field in the document sidebar.
listColumnbooleanfalseShow as a default column in list views.
searchablebooleanfalseIndex 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
},
Previous
URL
Next
Select