Overview
While Sanity allows developers to build content models directly by defining fields inside each document type, we actually use what we call islands to organize our field creation and make reusable logic that we can extend to our whole project.
This is what a standard field definition would look like:
defineField({ name: 'title', title: 'Title', type: 'string', description: 'Introduce your description here', options: {...}, validation: ..., hidden: ...})This implies giving a lot of information to the field each time it’s defined, and many of these fields will be the backbone of our project and we’ll use them a lot throughout the application.
This is why we opted by making reusable .js files that contain all of this information as a template which can be overwritten when using it, if needed.
For instance, that same title field is in our title.js island:
const title = (payload) => { const { title = 'Title', hidden, required = false, localized = true, initialValue, options, validation, ...additional } = payload || {}
console.log(title)
const fieldProperties = { name: 'title', title, options, type: 'string', hidden: hidden || false, localized, ...(additional && additional), initialValue, validation: (rule) => { if(validation){ return validation; } return rule.custom(resolveRequired(required, title)) }, } return localized ? [defineLocalized(fieldProperties)] : [defineField(fieldProperties)]}export {title}Notice here we send a lot of values as a payload and apply either those or the template values to our field. The localized property is our way of dealing with multiple languages, and it’s explained here.
Our islands are easily used in Schema definitions.
import {metaAttributes} from '@attributes/meta_attributes.js'import {title} from '@island/title.js'import {modules} from '@modules/common/index.js'import { heros } from '@modules/hero'export default { name: 'pages', title: 'Pages', type: 'document', fields: [ ...title(), ...metaAttributes({showImage : false , showHeaderFooter : { header : true, footer: true} }), ...heros(), ...modules(), ], preview: { select: { title: 'title.en', subtitle : 'attributes.slug' }, prepare(selection) { return { title: selection.title, subtitle: selection.subtitle, } }, },}We just need to import the island and call it in our type definition. This is how our pages definition looks like, and here we have used the title island with no custom values.
Creating new islands
Section titled “Creating new islands”Whenever we feel that a field will be reused by several document types, we create a new island for it and use that island in our type definition.
It’s as easy as defining a payload, basic values and a defineField or even another island to return.