Skip to content

text field

A text() field stores short, single-line string values. It is the most common field type in VexCMS — used for titles, names, slugs, URLs, email addresses, and any other short text value.

| Option | Type | Default | Description | |--------|------|---------|-------------| | label | string | "" | Display label in the admin panel. Inferred from the field key by defineCollection when left empty. | | required | boolean | false | When false, the field is wrapped in v.optional(…) in the schema and the form allows leaving it empty. | | defaultValue | string | "" | Pre-filled value shown in the admin form when creating a new document. | | min | number \| [number, string] | — | Minimum character length constraint. Bare number, or a [value, customError] tuple for a custom error message. | | max | number \| [number, string] | — | Maximum character length constraint. Bare number, or a [value, customError] tuple. | | index | string \| { name, additionalFields?, draftStatus? } | — | Convex index name (generates .index(name, [fieldKey])), or the object form for a compound index. See Compound indexes below. | | searchIndex | { name: string, filterFields: string[] } | — | Full-text search index on this field. The field becomes the searchField. | | description | string | — | Helper text shown below the field input. | | admin.hidden | boolean | false | Hides the field from the admin edit form entirely. | | admin.readOnly | boolean | false | Renders the input as non-interactive — value is visible but cannot be changed. | | admin.position | "main" \| "sidebar" | "main" | Column in the admin edit layout where this field appears. | | admin.width | string | "100%" | CSS width of the field in the edit form (e.g. "50%", "240px"). Meaningful as a child of row(); below a narrow form column every field renders full width regardless. | | admin.cellAlignment | "left" \| "center" \| "right" | "left" | Horizontal alignment of the value in the list-view table cell. | | admin.placeholder | string | — | Placeholder text shown when the input is empty. | | admin.description | string | — | Helper text displayed below the field input. | | admin.condition | (props: { data, siblingData }) => boolean | — | Shows the field only while this returns true. A throwing condition counts as met. Hidden fields skip required/validate on the server too. See the Conditional fields & masks guide. | | admin.mask | string \| string[] \| { mask, storeMasked?, options? } \| false | — | An opt-in use-mask-input pattern, or false to opt out of a project-wide default set via fields.inputMasks.text. See the Conditional fields & masks guide. |

The object form of index declares a compound index led by the field it’s set on: author’s index names status as additionalFields, so the generated index covers both fields in order.

author: relationship({ collection: users, index: { name: "by_author_status", additionalFields: ["status"] } })
// .index("by_author_status", ["author", "status"])

A field-level index wins over a collection-level indexes entry of the same name — every field setting wins over its collection’s. Pass the collection slug and the field’s own key as generics to type additionalFields against the collection’s other fields:

author: relationship<"articles", "author">({ collection: users, index: { name: "by_author_status", additionalFields: ["status"] } })

For an index that does not lead with a single field’s own key, declare it in the collection’s indexes instead.

// Optional (required: false — default)
title: v.optional(v.string())
// Required with length constraints
slug: v.string()
// With a database index
slug: v.string(),
.index("by_slug", ["slug"])
// With a search index
title: v.string(),
.searchIndex("search_title", { searchField: "title", filterFields: ["status"] })
import { defineCollection, text } from "@vexcms/core";
const posts = defineCollection({
slug: "posts",
fields: {
// Minimal — label inferred from key ("Title")
title: text(),
// Required slug with length validation and a database index
slug: text({ required: true, min: [3, "Slug too short"], max: 100, index: "by_slug" }),
// Author name with a placeholder hint
authorName: text({
required: true,
admin: { width: "50%", placeholder: "e.g. Jane Smith" },
}),
// Pre-filled default for a canonical URL
canonicalUrl: text({ defaultValue: "https://example.com" }),
},
});

The text field renders as a single-line <input type="text"> in the edit form. placeholder and description are displayed inline. In the list-view table, the raw string value is shown (left-aligned by default). When a field is designated as useAsTitle, its cell renders as a clickable link to the document edit page.