> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frogbot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Text Field

> Store strings and configure text inputs, validation, and slug generation.

The Text field stores a string and provides a text input in the admin panel. For web addresses, phone numbers and barcodes, use a [URL](https://docs.frogbot.ai/fields/url.md), [Phone](https://docs.frogbot.ai/fields/phone.md) or [Barcode](https://docs.frogbot.ai/fields/barcode.md) field. To have AI write the text from other fields, use an [AI field](https://docs.frogbot.ai/fields/ai.md).

```ts theme={"theme":"tokyo-night"}
import type { Field } from 'frogbot'

export const PageTitle: Field = {
  name: 'pageTitle',
  type: 'text',
  required: true,
}
```

## Config options

| Option | Type | Default | Description |
| - | - | - | - |
| `name` | `string` | Required | Property name used in stored records and APIs. See [field names](https://docs.frogbot.ai/fields/overview.md#field-names). |
| `type` | `'text'` | Required | Selects the Text field. |
| `hasMany` | `boolean` | `false` | Stores an ordered array of strings instead of one string. |
| `minLength` | `number` | — | Minimum length of each string, checked by the built-in validator. |
| `maxLength` | `number` | Root `defaultMaxTextLength` (`40000`) | Maximum length of each string, checked by the built-in validator. |
| `minRows` | `number` | — | Minimum number of strings when a value is present. Only with `hasMany: true`. |
| `maxRows` | `number` | — | Maximum number of strings when a value is present. Only with `hasMany: true`. |
| `label` | `string \| Record<string, string> \| (({ i18n, t }) => string) \| false` | Generated from `name` | Label shown in the admin panel. Use an object keyed by language code for translations, or `false` to hide the label. |
| `required` | `boolean` | `false` | Rejects an empty value in the default validation. |
| `unique` | `boolean` | `false` | Creates a unique database index for the field path. See [indexes](https://docs.frogbot.ai/database/indexes.md). SQL adapters reject `unique` together with `hasMany: true`. |
| `index` | `boolean` | `false` | Creates a database index for faster queries. `unique` already creates one. |
| `localized` | `boolean` | `false` | Stores one value per configured locale. Ignored without [localization](https://docs.frogbot.ai/configuration/localization.md) or inside a localized parent. |
| `defaultValue` | `string \| string[]` or `({ locale, req, user }) => value` | — | Value used when the field has no value. See [default values](https://docs.frogbot.ai/fields/overview.md#default-values). |
| `validate` | `Validate` | Built-in validator | Replaces the built-in validator. See [validation](https://docs.frogbot.ai/fields/overview.md#validation). |
| `hooks` | `{ beforeValidate, beforeChange, afterChange, afterRead, beforeDuplicate }` | — | Arrays of `FieldHook` functions for each phase. See [field hooks](https://docs.frogbot.ai/hooks/fields.md). |
| `access` | `{ create, read, update }` | Allowed | `FieldAccess` functions for each operation. See [field access control](https://docs.frogbot.ai/access-control/fields.md). |
| `hidden` | `boolean` | `false` | Removes the field from API responses and the admin panel while keeping stored data. |
| `saveToJWT` | `boolean \| string` | `false` | In an auth-enabled collection, copies the value into the user's token. A string sets the token key. See [token data](https://docs.frogbot.ai/authentication/token-data.md). |
| `virtual` | `boolean \| string` | `false` | `true` skips database storage; a string reads a value through a relationship path. Virtual fields are read-only in the admin panel unless `admin.readOnly` is `false`. See [virtual fields](https://docs.frogbot.ai/fields/overview.md#virtual-field-configuration). |
| `disableDuplicate` | `boolean` | `false` | Leaves the value out when a document is duplicated, so the copy uses `defaultValue`. |
| `admin` | `object` | — | Admin-panel options. See [Admin options](#admin-options). |
| `custom` | `Record<string, any>` | — | Server-only metadata for your own code and plugins. Use `admin.custom` for data the browser also needs. |
| `jsonSchema` | `Array<({ jsonSchema }) => JSONSchema4>` | — | Functions that modify the JSON Schema used by [type generation](https://docs.frogbot.ai/typescript/generating-types.md). |

## Admin options

Text fields support the standard [field admin options](https://docs.frogbot.ai/fields/overview.md#admin-options), plus these options:

| Option | Type | Default | Description |
| - | - | - | - |
| `placeholder` | `string \| Record<string, string>` | — | Placeholder text. Use an object keyed by language code for translations. |
| `autoComplete` | `string` | — | Value of the input's `autocomplete` attribute. |
| `rtl` | `boolean` | Follows the locale | `true` forces right-to-left text. `false` keeps left-to-right text in a right-to-left locale. When unset, right-to-left locales render right-to-left. |

## Example

```ts theme={"theme":"tokyo-night"}
import type { CollectionConfig } from 'frogbot'

export const Pages: CollectionConfig = {
  slug: 'pages',
  fields: [
    {
      name: 'pageTitle',
      type: 'text',
      minLength: 3,
      required: true,
      admin: {
        placeholder: 'About us',
      },
    },
  ],
}
```

## Custom components

### Field

#### Server component

```tsx theme={"theme":"tokyo-night"}
import { TextField } from '@frogbotai/ui/admin'
import type { TextFieldServerProps } from 'frogbot'

export const CustomTextFieldServer = (props: TextFieldServerProps) => (
  <TextField
    field={props.clientField}
    path={props.path}
    permissions={props.permissions}
    schemaPath={props.schemaPath}
  />
)
```

#### Client component

```tsx theme={"theme":"tokyo-night"}
'use client'

import { TextField } from '@frogbotai/ui/admin'
import type { TextFieldClientProps } from 'frogbot'

export const CustomTextFieldClient = (props: TextFieldClientProps) => (
  <TextField {...props} />
)
```

### Label

#### Server component

```tsx theme={"theme":"tokyo-night"}
import { FieldLabel } from '@frogbotai/ui/admin'
import type { TextFieldLabelServerProps } from 'frogbot'

export const CustomTextLabelServer = ({ clientField, path }: TextFieldLabelServerProps) => (
  <FieldLabel
    label={clientField.label || clientField.name}
    path={path}
    required={clientField.required}
  />
)
```

#### Client component

```tsx theme={"theme":"tokyo-night"}
'use client'

import { FieldLabel } from '@frogbotai/ui/admin'
import type { TextFieldLabelClientProps } from 'frogbot'

export const CustomTextLabelClient = ({ field, path }: TextFieldLabelClientProps) => (
  <FieldLabel label={field.label || field.name} path={path} required={field.required} />
)
```

## Slug field

The Slug field is experimental and may change or be removed in a future release.

`slugField` returns the built-in Slug field (`type: 'slug'`): a unique, indexed string generated from another field, shown in the sidebar. Editors can unlock it to edit it, or regenerate it on demand in the admin panel.

```ts theme={"theme":"tokyo-night"}
import { slugField } from 'frogbot'
import type { CollectionConfig } from 'frogbot'

export const Pages: CollectionConfig = {
  slug: 'pages',
  fields: [
    { name: 'title', type: 'text', required: true },
    slugField({ useAsSlug: 'title' }),
  ],
}
```

The slug fills once while it's empty and then stays, so editing the title never changes a live URL:

* On create, an empty slug is generated from `useAsSlug`. Without a source value, it becomes `<singular>-<N>`, such as `pages-1`, so a record always has one.
* An explicit value wins and goes through `slugify`, so `Hello World` becomes `hello-world`. A value another record already uses is rejected.
* Generated values skip slugs other records already use.

`slugField(options)` is the same as writing `{ name: 'slug', type: 'slug', ...options }`. Besides the usual field options, such as `label`, `localized`, `required`, `unique`, `index`, `defaultValue` and `admin`, it takes:

| Option | Type | Default | Description |
| - | - | - | - |
| `name` | `string` | `'slug'` | Name of the field. |
| `useAsSlug` | `string` | — | Top-level field in the same collection the slug is generated from. |
| `unique` | `boolean` | `true` | Set `false` to drop the unique index, for example for a per-tenant compound index. |
| `slugify` | `({ data, req, valueToSlugify }) => string \| undefined \| Promise<string \| undefined>` | Built-in slugify | Replaces the slug-generation function. See [Custom slugify function](#custom-slugify-function). |

### Custom slugify function

The function receives the complete document data, the source value, and the framework-issued `FrogBotRequest`. It may return a string or `undefined`, synchronously or asynchronously. Rejections stop the save.

```ts theme={"theme":"tokyo-night"}
import { slugField } from 'frogbot'

export const pageSlug = slugField({
  useAsSlug: 'title',
  slugify: async ({ req, valueToSlugify }) => {
    await req.frogbot.count({ collection: 'pages', overrideAccess: true })

    return typeof valueToSlugify === 'string'
      ? valueToSlugify
          .normalize('NFKD')
          .toLowerCase()
          .trim()
          .replace(/[^a-z0-9]+/g, '-')
          .replace(/(^-|-$)/g, '')
      : undefined
  },
})
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.