# Improving the editorial experience https://www.sanity.io/learn/course/studio-excellence/improving-the-editorial-experience.md Elevate the basic editorial experience with field titles, descriptions, validation, conditional fields, field groups, and document list previews. ## Consistency Helping content creators create content consistently is simpler when they're presented with fewer options and helpful guardrails. 1. Make slug creation simpler by adding a `source` for generating the slug field value ```typescript:apps/studio/schemaTypes/eventType.ts // Replace "slug" in the array of fields: defineField({ name: 'slug', type: 'slug', options: {source: 'name'}, }), ``` Text input fields can contain any string. Avoid accidental duplicates or misspellings by providing preset options. 1. Limit the `eventType` field to a few options by providing a list of values: ```typescript:apps/studio/schemaTypes/eventType.ts // Replace "eventType" in the array of fields: defineField({ name: 'eventType', type: 'string', options: { list: ['in-person', 'virtual'], layout: 'radio', }, }), ``` If new documents could benefit from sensible default values which are often correct, an initial value can be set at the field level. Note that this won't affect existing documents. But every new document will now start with this value. 1. Set an initial value for the `doorsOpen` field to `60` ```typescript:apps/studio/schemaTypes/eventType.ts // Replace "doorsOpen" in the array of fields: defineField({ name: 'doorsOpen', type: 'number', initialValue: 60, }), ``` ### Adding context Adding more context and intentionality to fields can be very helpful for content teams. With only its name to describe it, this field fails to give the author context for its use. Adding descriptions to fields helps clarify their intention and guide the content creation experience. 1. Add a description to the `doorsOpen` field ```typescript:apps/studio/schemaTypes/eventType.ts // Replace "doorsOpen" in the array of fields: defineField({ name: 'doorsOpen', description: 'Number of minutes before the start time for admission', type: 'number', initialValue: 60, }), ``` ## Validation Structured content is more trustworthy when it is validated. Validation rules can also give content creators more confidence to press Publish. 1. See [Validation](https://www.sanity.io/learn/studio/validation) in the documentation. 1. Make the `slug` field required ```typescript:apps/studio/schemaTypes/eventType.ts // Replace "slug" in the array of fields: defineField({ name: 'slug', type: 'slug', options: {source: 'name'}, validation: (rule) => rule .required() .error(`Required to generate a page on the website`), }), ``` Note that the error message can be customized so that authors better understand the context of why a warning or error is being displayed. Custom rules have access to the entire document so that fields may be validated against one another. They should return a `string` in case of an error or `true` once the field is considered valid. 1. Using a custom validation rule, make sure that virtual events do not have a venue ```typescript:apps/studio/schemaTypes/eventType.ts // Replace "venue" in the array of fields: defineField({ name: 'venue', type: 'reference', to: [{type: 'venue'}], validation: (rule) => rule.custom((value, context) => { if (value && context?.document?.eventType === 'virtual') { return 'Only in-person events can have a venue' } return true }), }), ``` ## Conditionally hidden and read-only It can often be useful to hide less common or more complex fields until they are required. While `hidden` and `readOnly` can be set to `true` or `false` – they can also accept a function to apply some logic. 1. See [Conditional fields](https://www.sanity.io/learn/studio/conditional-fields) in the documentation. 1. Hide the `slug` field if the `name` field is empty ```typescript:apps/studio/schemaTypes/eventType.ts // Replace "slug" in the array of fields: defineField({ name: 'slug', type: 'slug', options: {source: 'name'}, validation: (rule) => rule.required().error(`Required to generate a page on the website`), hidden: ({document}) => !document?.name, }), ``` 1. Set `venue` to `readOnly` if the field does not have a value and the event type is virtual ```typescript:apps/studio/schemaTypes/eventType.ts // Replace "venue" in the array of fields: defineField({ name: 'venue', type: 'reference', to: [{type: 'venue'}], readOnly: ({value, document}) => !value && document?.eventType === 'virtual', validation: (rule) => rule.custom((value, context) => { if (value && context?.document?.eventType === 'virtual') { return 'Only in-person events can have a venue' } return true }), }), ``` Note that "hidden" only affects the Studio interface. Fields will still retain their values whether they are visible in the Studio or not. ## Group fields together The document form is still one long column of fields. Let's introduce a set of field groups (think of them like tabs) to tidy up the form. 1. See [Field Groups](https://www.sanity.io/learn/studio/field-groups) in the documentation. 1. Create two Groups in the `event` document schema: `details` and `editorial` ```typescript:apps/studio/schemaTypes/eventType.ts // Above the "fields" array groups: [ {name: 'details', title: 'Details'}, {name: 'editorial', title: 'Editorial'}, ], ``` 1. Assign each field to one or more of these Groups ```typescript:schemaTypes/eventType.ts // Assign each field to one group group: 'details', // or several! group: ['details', 'editorial'], ``` The image and details fields make the most sense in "editorial" and the rest in "details." You could customize further by adding an icon to each group and setting one active by default. ## Document list previews By default, documents are indicated with a plain icon and a "best guess" at the document's title. The first and simplest thing we can do is give documents of a certain type a unique icon so that every document list and search result will use them by default. ### Icons 1. **Run** the following in the terminal to install the `@sanity/icons` package ```sh # in apps/studio pnpm add @sanity/icons ``` 1. **Update** your document schema types to give them default icons ```tsx:apps/studio/schemaTypes/eventType.ts import {CalendarIcon} from '@sanity/icons' export const eventType = defineType({ name: 'event', title: 'Event', icon: CalendarIcon, // ...all other settings }) ``` ```tsx:apps/studio/schemaTypes/venueType.ts import {PinIcon} from '@sanity/icons' export const venueType = defineType({ name: 'venue', title: 'Venue', icon: PinIcon, // ...all other settings }) ``` ```tsx:apps/studio/schemaTypes/artistType.ts import {UsersIcon} from '@sanity/icons' export const artistType = defineType({ name: 'artist', title: 'Artist', icon: UsersIcon, // ...all other settings }) ``` ### Title, subtitle and media Let's go further! So that our documents are even more discoverable, you can update the `preview` property for our document type. In `preview`, values are "selected" from the document and then fed into the document preview's `title`, `subtitle`, and `media` slots. 1. See [List Previews](https://www.sanity.io/learn/studio/previews-list-views) in the documentation. 1. **Update** the `preview` attribute to render a richer preview ```typescript:apps/studio/schemaTypes/eventType.ts // After the "fields" array preview: { select: { title: 'name', subtitle: 'headline.name', media: 'image', }, }, ``` Take a look at your document list now. It's much easier to discern the document types at a glance. There are other values in the document that would be useful in list previews. With the `prepare` function, you can modify values before returning them to the preview. Update your event type’s preview configuration to the below: ```typescript:apps/studio/schemaTypes/eventType.ts // Update the preview key in the schema preview: { select: { name: 'name', venue: 'venue.name', artist: 'headline.name', date: 'date', image: 'image', }, prepare({name, venue, artist, date, image}) { const nameFormatted = name || 'Untitled event' const dateFormatted = date ? new Date(date).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', }) : '' return { title: artist ? `${nameFormatted} (${artist})` : nameFormatted, subtitle: venue ? `${dateFormatted} @ ${venue}` : dateFormatted, media: image || CalendarIcon, } }, }, ``` ## Review Take a step back and compare the editorial experience with the simple form you had initially. Content creators can discern the different types of documents and more easily create trustworthy content. You could go further by adding icons to the `artist` and `venue`-type documents. This is a much better experience, and it's only the beginning.