# Model configuration

<Callout type="info">
  This is the documentation for the latest version of Next Admin. If you are using an older version (`<5.0.0`), please refer to the [documentation](https://next-admin.js.org/v4/docs)
</Callout>

To configure your models, you can use the `model` property of the [NextAdmin options](https://next-admin.js.org/docs/api/options) parameter.

This property allows you to configure everything about how your models, their fields, their relations are displayed and edited.

Example for a schema with a `User`, `Post` and `Category` model:

```tsx copy
import { NextAdminOptions } from "@premieroctet/next-admin";

export const options: NextAdminOptions = {
  /* Your other options here */
  model: {
    User: {
      toString: (user) => `${user.name} (${user.email})`,
      title: "Users",
      icon: "UsersIcon",
      list: {
        display: ["id", "name", "email", "posts", "role", "birthDate"],
        search: ["name", "email"],
        filters: [
          {
            name: "is Admin",
            active: false,
            value: {
              role: {
                equals: "ADMIN",
              },
            },
          },
        ],
      },
      edit: {
        display: [
          "id",
          "name",
          "email",
          "posts",
          "role",
          "birthDate",
          "avatar",
        ],
      },
    },
    Post: {
      toString: (post) => `${post.title}`,
    },
    Category: {
      title: "Categories",
      icon: "InboxStackIcon",
      toString: (category) => `${category.name}`,
      list: {
        display: ["name", "posts"],
      },
      edit: {
        display: ["name", "posts"],
      },
    },
  },
};
```

It takes as **key** a model name of your schema as **value** an object to configure it.

By default, if no models are defined, they will all be displayed in the admin. If you want more control, you have to define each model individually as empty objects or with the following properties:

<OptionsTable
  options={[
    {
      name: "toString",
      description:
        "a function that is used to display your record in related list",
      defaultValue: "id field",
    },
    {
      name: "aliases",
      description:
        "an object containing the aliases of the model fields as keys, and the field name",
    },
    {
      name: "title",
      description:
        "a string used to display the model name in the sidebar and in the section title",
      defaultValue: "Model name",
    },
    {
      name: "list",
      description: (
        <>
          an object containing the list options (see{" "}
          <a href="#list-property">list property</a>)
        </>
      ),
    },
    {
      name: "edit",
      description: (
        <>
          an object containing the edit options (see{" "}
          <a href="#edit-property">edit property</a>)
        </>
      ),
    },
    {
      name: "actions",
      description: (
        <>
          {" "}
          an array of actions (see{" "}
          <a href="#actions-property">actions property</a>)
        </>
      ),
    },
    {
      name: "icon",
      description: (
        <>
          the outline{" "}
          <a href="https://heroicons.com/" target="_blank">
            HeroIcon
          </a>{" "}
          name displayed in the sidebar and pages title
        </>
      ),
    },
    {
      name: "permissions",
      description: "an array to specify restricted permissions on model",
      defaultValue: "[`create`, `edit`, `delete`]",
    },
  ]}
/>

You can customize the following for each model:

## `list` property

This property determines how your data is displayed in the [list View](https://next-admin.js.org/docs/glossary#list-view)

<OptionsTable
  options={[
    {
      name: "search",
      type: "Array",
      description:
        "an array of searchable fields. You can search on nested fields by concatenating them with a dot, for example author.name",
      defaultValue: "All scalar fields are searchable by default",
    },
    {
      name: "display",
      type: "Array",
      description: (
        <>
          an array of fields that are displayed in the list. You can also pass an object, check <a href="#virtual-list-fields">virtual list fields</a>
        </>
      ),
      defaultValue: "All scalar fields are displayed by default",
    },
    {
      name: "orderField",
      type: "String",
      description:
        "the field to use for ordering the list, it must be a numeric field. ⚠️ When enabled, it will disable all other types of sorting.",
    },
    {
      name: "fields",
      type: "Object",
      description: (
        <>
          an object containing the model fields as keys, and customization
          values (see <a href="#listfields-property">fields property</a>)
        </>
      ),
    },
    {
      name: "copy",
      type: "Array",
      description: "an array of fields that are copyable into the clipboard",
      defaultValue: "undefined - no field is copyable by default",
    },
    {
      name: "defaultSort",
      type: "Object",
      description:
        "an optional object to determine the default sort to apply on the list",
    },
    {
      name: "defaultSort.field",
      type: "String",
      description:
        "the model's field name to which the sort is applied. It is mandatory",
    },
    {
      name: "defaultSort.direction",
      type: "String",
      description: "the sort direction to apply. It is optional",
    },
    {
      name: "filters",
      type: "Array",
      description: (
        <>
          {" "}
          define a set of Prisma filters that user can choose in list (see{" "}
          <a href="#listfilters-property">filters</a>)
        </>
      ),
    },
    {
      name: "exports",
      type: "Object",
      description: (
        <>
          an object or array of export - containing export url (see{" "}
          <a href="#listexports-property">exports</a>)
        </>
      ),
    },
    {
      name: "defaultListSize",
      type: "Number",
      description: (
        <>
          the default number of items to display per page. It is optional. Defaults to 10.
        </>
      ),
    },
  ]}
/>
<Callout type="info">
  The `search` property is only available for [`scalar`
  fields](https://www.prisma.io/docs/orm/reference/prisma-schema-reference#model-field-scalar-types).
</Callout>

#### `list.fields` property

The `fields` property is an object that can have the following properties:

<OptionsTable
  options={[
    {
      name: "formatter",
      type: "Function",
      description: (
        <>
          a function that takes the field value as a parameter, and returns a
          JSX node. It also accepts a second argument which is the{" "}
          <a href="#nextadmin-context">NextAdmin context</a>
        </>
      ),
    },
    {
      name: "sortBy",
      type: "String",
      description:
        "available only on many-to-one models. The name of a field in the related model to apply the sort to. Defaults to the id field of the related model",
    },
  ]}
/>

##### `list.filters` property

The `filters` property allow you to define a set of Prisma filters that user can apply on list page. It's an array of the type below:

<OptionsTable
  options={[
    {
      name: "name",
      type: "String",
      description: "a unique name for the filter",
    },
    {
      name: "active",
      type: "Boolean",
      description: "a boolean to set the filter active by default",
      defaultValue: "false",
    },
    {
      name: "value",
      type: "String",
      description: (
        <>
          a where clause Prisma filter of the related model (e.g.{" "}
          <a href="https://www.prisma.io/docs/orm/reference/prisma-client-reference#filter-conditions-and-operators">
            Prisma operators
          </a>
          )
        </>
      ),
    },
    {
      name: "group",
      type: "String",
      description:
        "an id that will be used to give filters with the same group name a radio like behavior",
    },
  ]}
/>

It can also be an async function that returns the above type so that you can have a dynamic list of filters.

#### `list.exports` property

The `exports` property is available in the `list` property. It's an object or an array of objects that can have the following properties:

<OptionsTable
  options={[
    {
      name: "format",
      type: "String",
      description:
        "a string defining the format of the export. It's used to label the export button",
      defaultValue: "undefined",
    },
    {
      name: "url",
      type: "String",
      description: "a string defining the URL of the export action",
      defaultValue: "undefined",
    },
  ]}
/>

<Callout type="info">
  The `exports` property does not account for active filters. If you want to
  export filtered data, you need to add the filters to the URL or in your export
  action.
</Callout>

## `edit` property

This property determines how your data is displayed in the [edit view](https://next-admin.js.org/docs/glossary#edit-view)

{" "}

<OptionsTable
  options={[
    {
      name: "display",
      type: "Array",
      description: (
        <>
          {" "}
          an array of fields that are displayed in the form. It can also be an
          object that will be displayed in the form of a notice (see{" "}
          <a href="#notice">notice</a>)
        </>
      ),
      defaultValue: "all scalar fields are displayed",
    },
    {
      name: "styles",
      type: "Object",
      description: (
        <>
          an object containing the styles of the form (see{" "}
          <a href="#editstyles-property">styles</a>){" "}
        </>
      ),
    },
    {
      name: "fields",
      type: "Object",
      description: (
        <>
          an object containing the model fields as keys, and customization
          values (see <a href="#editfields-property">fields property</a>)
        </>
      ),
    },
    {
      name: "submissionErrorMessage",
      type: "String",
      description:
        "a message displayed if an error occurs during the form submission",
      defaultValue: "'Submission error'",
    },
  ]}
/>

#### `edit.styles` property

The `styles` property is available in the `edit` property.

<Callout emoji="⚠️">
  If your options are defined in a separate file, make sure to add the path to
  the `content` property of the `tailwind.config.js` file (see [TailwindCSS
  configuration](https://next-admin.js.org/docs/getting-started#tailwindcss-configuration)).
</Callout>

<OptionsTable
  options={[
    {
      name: "_form",
      type: "String",
      description: "a string defining the classname of the form",
    },
    {
      name: "...",
      type: "String",
      description:
        "all fields of the model, wwith the field name as the key and the classname as the value",
    },
  ]}
/>

Here is an example of using `styles` property:

```ts
styles: {
  _form: "form-classname",
  ... // all fields
};
```

#### `edit.fields` property

This property can be defined for each field of the corresponding model.
When you define a field, use the field's name as the key and the following object as the value:

<OptionsTable
  options={[
    {
      name: "validate",
      type: "Function",
      description:
        "a function that takes the field value as a parameter, and returns a boolean",
    },
    {
      name: "format",
      type: "String",
      description: (
        <>
          a string defining an OpenAPI field format, overriding the one set in
          the generator. An extra <code>file</code> format can be used to be
          able to have a file input
        </>
      ),
    },
    {
      name: "input",
      type: "React Element",
      description: (
        <>
          a React Element that should receive{" "}
          <a href="#custominputprops">CustomInputProps</a>. For App Router, this
          element must be a client component
        </>
      ),
    },
    {
      name: "handler",
      type: "Object",
      description: "",
    },
    {
      name: "handler.get",
      type: "Function",
      description:
        "a function that takes the field value as a parameter and returns a transformed value displayed in the form",
    },
    {
      name: "handler.upload",
      type: "Function",
      description: (
        <>
          an async function that is used only for formats <code>file</code> and{" "}
          <code>data-url</code>. It takes a Buffer object and an information
          object containing <code>name</code> and <code>type</code> properties
          as parameters and must return a string. It has <code>context</code> as
          a parameter, which holds record information such as the resource ID,
          which does not exist during record creation. It can be useful to
          upload a file to a remote provider
        </>
      ),
    },
    {
      name: "handler.uploadErrorMessage",
      type: "String",
      description:
        "an optional string displayed in the input field as an error message in case of a failure during the upload handler",
    },
    {
      name: "handler.deleteFile",
      type: "Function",
      description: (
        <>
          an async function that is used to remove a file from a remote provider. Takes the file URI as an argument.
        </>
      )
    },
    {
      name: "optionFormatter",
      type: "Function",
      description:
        "only for relation fields, a function that takes the field values as a parameter and returns a string. Useful to display your record in related list",
    },
    {
      name: "tooltip",
      type: "String",
      description: "A tooltip content to display for the field ",
    },
    {
      name: "helperText",
      type: "String",
      description: "a helper text that is displayed underneath the input",
    },
    {
      name: "disabled",
      type: "Boolean",
      description: "a boolean to indicate that the field is read only",
    },
    {
      name: "display",
      type: "String",
      description: (
        <>
          {" "}
          only for relation fields, indicate which display format to use between{" "}
          <code>list</code>, <code>table</code> or <code>select</code>
        </>
      ),
      defaultValue: "select",
    },
    {
      name: "required",
      type: "Boolean",
      description: (
        <>
          a true value to force a field to be required in the form, note that if
          the field is required by the Prisma schema, you cannot set{" "}
          <code>required</code> to false
        </>
      ),
    },
    {
      name: "relationOptionFormatter",
      type: "Function",
      description: (
        <>
          same as <code>optionFormatter</code>, but used to format data that
          comes from an{" "}
          <a href="https://www.prisma.io/docs/orm/prisma-schema/data-model/relations/many-to-many-relations#explicit-many-to-many-relations">
            explicit many-to-many
          </a>{" "}
          relationship
        </>
      ),
    },
    {
      name: "orderField",
      type: "String",
      description: (
        <>
          the field to use for relationship sorting. This allows to drag and
          drop the related records in the <code>list</code> display
        </>
      ),
    },
    {
      name: "relationshipSearchField",
      type: "String",
      description: (
        <>
          a field name of the explicit many-to-many relation table to apply the
          search on. See{" "}
          <a href="https://www.prisma.io/docs/orm/prisma-schema/data-model/relations/many-to-many-relations#explicit-many-to-many-relations">
            handling explicit many-to-many
          </a>
        </>
      ),
    },
    {
      name: "visible",
      type: "Function",
      description:
        "a function that takes the resource value as a parameter, and returns a boolean that shows or hides the field in the edit form",
    },
    {
      name: "pagination",
      type: "object",
      description: (
        <>
          An object (can be empty) that adds pagination for multi select fields (with <code>display</code> set to either <code>table</code> or <code>list</code>).
          It accepts a property <code>perPage</code> defining the amount of items per page (defaults to 20)
        </>
      )
    },
    {
      name: "maxLength",
      type: "Number",
      description: "a number that defines the maximum number of items a scalar array can contain."
    }
  ]}
/>

### `edit.hooks` property

The `hooks` property allows you to intercept the values of a form, before and after its insertion in the database.
It is an object that can have the following properties:

<OptionsTable
  options={[
    {
      name: "beforeDb",
      type: "Function",
      description:
        "a function that takes the form values, the insertion mode (create or edit) and the request object as parameters. It must return a promise with the form values. You can throw a [HookError]() in case of an error, which will cause an early return in the handler with the appropriate response status and error message.",
    },
    {
      name: "afterDb",
      type: "Function",
      description:
        "a function that takes the insertion response, the insertion mode (create or edit) and the request as parameters. It returns the same data as the insertion response.",
    },
  ]}
/>

## `actions` property

The `actions` property is an array of objects that allows you to define a set of actions that can be executed on one or more records of the resource. On the list view, there is a default action for deletion. The object can have the following properties:

<OptionsTable
  options={[
    {
      name: "title",
      type: "String",
      description: "action title that will be shown in the action dropdown",
    },
    {
      name: "id",
      type: "String",
      description: "mandatory, action's unique identifier",
    },
    {
      name: "type",
      type: "String",
      description:
        "action type for client side actions, possible value is 'dialog' | 'server'. By default action with no type is executed on the server",
    },
    {
      name: "component",
      type: "ReactElement",
      description: (
        <>
          a React component that will be displayed in a dialog when the action
          is triggered. Its mandatory if the action type `dialog` is specified.
          See the{" "}
          <a href="#clientactiondialogcontentprops">
            ClientActionDialogContentProps
          </a>{" "}
          for the props passed to the component.<br /><br />
          For a server side action, it is used inside the message shown at the top of the page
          after the action execution. It receives a prop <code>message</code> containing the
          message to be displayed. Useful in case you want to display custom content,
          for example as Markdown.
        </>
      ),
    },
    {
      name: "className",
      type: "string",
      description: (
        <>
          class name applied to the dialog displayed when the action type is set
          to 'dialog'.
        </>
      ),
    },
    {
      name: "action",
      type: "Function",
      description: (
        <>
          an async function that will be triggered when selecting the action in
          the dropdown. Its mandatory if the action type is not specified. Can
          return a message object to display a message after the action is done
          or throw an error to display a message
        </>
      ),
    },
    {
      name: "canExecute",
      type: "Function",
      description: (
        <>
          a function that takes a record as a parameter and returns a boolean.
          It is used to determine if the action can be executed on the record
        </>
      ),
    },
    {
      name: "successMessage",
      type: "String",
      description:
        "a message that will be displayed when the action is successful if action doesn't return a message object",
    },
    {
      name: "errorMessage",
      type: "String",
      description:
        "a message that will be displayed when the action fails if action doesn't return a message object or throw an error with a message",
    },
    {
      name: "depth",
      type: "Number",
      description:
        "a number that defines the depth of the relations to select in the resource. Use this with caution, a number too high can potentially cause slower queries. Defaults to 2.",
    },
    {
      name: "icon",
      type: "String",
      description: (
        <>
          the outline{" "}
          <a href="https://heroicons.com/" target="_blank">
            HeroIcon
          </a>{" "}
          name displayed in the actions dropdown for this action
        </>
      ),
    }
  ]}
/>

## `middlewares` property

The `middlewares` property is an object of functions executed either before a record's update or deletion, where you can control if the deletion and update should happen or not. It can have the following properties:

<OptionsTable
  options={[
    {
      name: "edit",
      type: "Function",
      description:
        "a function that is called before the form data is sent to the database. It takes the submitted form data as its first argument, and the current value in the database as its second argument. If false is returned, the update will not happen.",
    },
    {
      name: "delete",
      type: "Function",
      description:
        "a function that is called before the record is deleted. It takes the record to delete as its only argument. If false is returned, the deletion will not happen.",
    },
  ]}
/>

## NextAdmin Context

The `NextAdmin` context is an object containing the following properties:

<OptionsTable
  options={[
    {
      name: "locale",
      type: "String",
      description: (
        <>
          The locale used by the calling page. (refers to the{" "}
          <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language">
            accept-language
          </a>{" "}
          header)
        </>
      ),
    },
    {
      name: "row",
      type: "Object",
      description: "The current row of the list view",
    },
  ]}
/>

## Notice

The edit page's form can display notice alerts. To do so, you can pass objects in the `display` array of the `edit` property of a model.
This can be useful to display alerts or information to the user when editing a record.
This object takes the following properties :

<OptionsTable
  options={[
    {
      name: "title",
      type: "String",
      description: "The title of the notice. This is mandatory",
    },
    {
      name: "id",
      type: "String",
      description: (
        <>
          A unique identifier for the notice that can be used to style it with
          the <code>styles</code> property. This is mandatory
        </>
      ),
    },
    {
      name: "description",
      type: "String",
      description: "The description of the notice. This is optional",
    },
  ]}
/>

Here is a quick example of usage.

Considering you have a model `User` with the following configuration:

```typescript filename=".../options.ts" copy {13-17}
export const options: NextAdminOptions = {
  basePath: "/admin",
  title: "⚡️ My Admin",
  model: {
    User: {
      /**
        ...some configuration
      **/
      edit: {
        display: [
          "id",
          "name",
          {
            title: "Email is mandatory",
            id: "email-notice",
            description: "You must add an email from now on",
          } as const,
          "email",
          "posts",
          "role",
          "birthDate",
          "avatar",
          "metadata",
        ],
        /** ... some configuration */
      },
    },
  },
};
```

In this example, the `email-notice` notice will be displayed in the form before the `email` field.
![Notice example](https://next-admin.js.org/docs/notice-exemple.png)

## CustomInputProps

Represents the props that are passed to the custom input component.

<OptionsTable
  options={[
    {
      name: "name",
      type: "String",
      description: "the field name",
    },
    {
      name: "value",
      type: "any",
      description: "the field value",
    },
    {
      name: "onChange",
      type: "Function",
      description: (
        <>
          {" "}
          a function taking a{" "}
          <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event">
            ChangeEvent
          </a>{" "}
          as a parameter
        </>
      ),
    },
    {
      name: "readonly",
      type: "Boolean",
      description: "boolean indicating if the field is editable or not",
    },
    {
      name: "rawErrors",
      type: "Array",
      description: "array of strings containing the field errors",
    },
    {
      name: "disabled",
      type: "Boolean",
      description: "boolean indicating if the field is disabled",
    },
    {
      name: "item",
      type: "any",
      description: <>the <b>initial</b> value of the resource being edited</>,
    },
  ]}
/>

You can access the current form data state using the `useFormData` hook imported from `@premieroctet/next-admin/utils`.

```tsx
import { useFormData } from '@premieroctet/next-admin/utils'

const { formData } = useFormData()
```

## HookError

The `HookError` is an error that can be thrown in the `beforeDb` hook of the `edit` property. It takes the following arguments in its constructor:

<OptionsTable
  options={[
    {
      name: "status",
      type: "Number",
      description: "the HTTP status code",
    },
    {
      name: "data",
      type: "Object",
      description:
        "an error object that must contain at least an error property which is a string",
    },
  ]}
/>

## ClientActionDialogContentProps

Represents the props that are passed to the custom dialog component.

<OptionsTable
  options={[
    {
      name: "resource",
      type: "String",
      description: "the current Prisma model name",
    },
    {
      name: "resourceId",
      type: "String | Number",
      description: "the selected record id",
    },
    {
      name: "data",
      type: "Record<string, ListDataFieldValue>",
      description: (
        <>
          A record of row's properties with{" "}
          <a href="#listdatafieldvalue">ListDataFieldValue</a> as value
        </>
      ),
    },
    {
      name: "onClose",
      type: "Function",
      description:
        "a function to close the dialog, it can receive a message object to display a message after the dialog is closed",
    },
  ]}
/>

## ListDataFieldValue

Represents a formatted value used by Next-Admin. It will have different shapes depending of the data type.

It will always have the following :

<OptionsTable
  options={[
    {
      name: "__nextadmin_formatted",
      type: "ReactNode",
      description:
        "A React Node used to have a custom rendering of the field, if needed",
    },
  ]}
/>

### Scalar

<OptionsTable
  options={[
    {
      name: "type",
      type: "`scalar`",
      description: "The type of data. It is always `scalar`",
    },
    {
      name: "value",
      type: "String | Number | boolean",
      description: "the value coming from the database",
    },
  ]}
/>

### Count

<OptionsTable
  options={[
    {
      name: "type",
      type: "`count`",
      description: "The type of data. It is always `count`",
    },
    {
      name: "value",
      type: "Number",
      description: "the value coming from the database",
    },
  ]}
/>

### Link

<OptionsTable
  options={[
    {
      name: "type",
      type: "`link`",
      description: "The type of data. It is always `link`",
    },
    {
      name: "value",
      type: "object",
      description: "the link data displayed in the link CTA",
    },
    {
      name: "value.label",
      type: "String",
      description: "the link CTA label",
    },
    {
      name: "value.url",
      type: "String",
      description: "the link CTA href",
    },
  ]}
/>

### Date

<OptionsTable
  options={[
    {
      name: "type",
      type: "`date`",
      description: "The type of data. It is always `date`",
    },
    {
      name: "value",
      type: "Date",
      description: "the value coming from the database",
    },
  ]}
/>

## Virtual list fields

It is possible to add custom columns in the list panel that are computed from the value of other columns. This is done by setting an object in the `list.display` array of a model. This object takes the following properties:

<OptionsTable
  options={[
    {
      name: "key",
      type: "String",
      description: "A unique key used to index the value in the data",
    },
    {
      name: "label",
      type: "String",
      description: (
        <>
          An optional explicit label for the table header. If a translation under the <code>model.your_resource.fields.your_key</code>" is provided, this translation will be used, else this label, otherwise the key.
        </>
      ),
    },
    {
      name: "dependsOn",
      type: "String[]",
      description: (
        <>
          An array of fields on the current resource that the virtual field depends on. You should typically include fields that are not already in the <code>display</code> array.
        </>
      ),
    },
    {
      name: "formatter",
      type: "Function",
      description: (
        <>
          A function that takes a <a href="#nextadmin-context">NextAdmin context</a> as a parameter, containing the raw data value. This function can either return a string or a react element (currently only for App Router). If a string is returned, a <code>type</code> property can be provided to alter the render of the cell in the table.
        </>
      ),
    },
    {
      name: "type",
      type: "String",
      description: (
        <>
          An optional type used to alter the render of the cell. Available values are <code>scalar</code>, <code>count</code>, <code>date</code> and <code>link</code>. If <code>link</code> is used, the <code>url</code> function must be provided.
        </>
      ),
      defaultValue: "scalar"
    },
    {
      name: "url",
      type: "Function",
      description: (
        <>
          A function that takes a <a href="#nextadmin-context">NextAdmin context</a> as a parameter, containing the raw data value. This function should return a string representing the URL to which the cell should link.
        </>
      ),
    }
  ]}
/>
