# Add drag-and-drop elements https://www.sanity.io/learn/course/visual-editing-with-next-js/add-drag-and-drop-elements.md Go beyond "click-to-edit" with additional affordances for rearranging arrays in your front end ### Add "related posts" to your posts 1. **Update** the `post` schema type fields to include an array of "related posts" to render at the bottom of your `post` type documents. ```typescript:src/sanity/schemaTypes/postType.ts export const postType = defineType({ // ...all other settings fields: [ // ...all other fields defineField({ name: "relatedPosts", type: "array", of: [{ type: "reference", to: { type: "post" } }], }), ], }); ``` 1. **Update** your single post query to return the array and resolve any references. ```typescript:src/sanity/lib/queries.ts export const POST_QUERY = defineQuery(`*[_type == "post" && slug.current == $slug][0]{ _id, title, body, mainImage, publishedAt, "categories": coalesce( categories[]->{ _id, slug, title }, [] ), author->{ name, image }, relatedPosts[]{ _key, // required for drag and drop ...@->{_id, title, slug} // get fields from the referenced post } }`); ``` 1. **Update** your types now that the GROQ query has changed. ```sh pnpm run typegen ``` 1. **Create** a new component to render the related Posts ```tsx:src/components/related-posts.tsx 'use client' import Link from 'next/link' import { createDataAttribute } from 'next-sanity' import { useOptimistic } from 'next-sanity/hooks' import { POST_QUERYResult } from '@/sanity/types' import { client } from '@/sanity/lib/client' const { projectId, dataset, stega } = client.config() export const createDataAttributeConfig = { projectId, dataset, baseUrl: typeof stega.studioUrl === 'string' ? stega.studioUrl : '', } export function RelatedPosts({ relatedPosts, documentId, documentType, }: { relatedPosts: NonNullable['relatedPosts'] documentId: string documentType: string }) { const posts = useOptimistic< NonNullable['relatedPosts'] | undefined, NonNullable >(relatedPosts, (state, action) => { if (action.id === documentId && action?.document?.relatedPosts) { // Optimistic document only has _ref values, not resolved references return action.document.relatedPosts.map( (post) => state?.find((p) => p._key === post._key) ?? post ) } return state }) if (!posts) { return null } return ( ) } ``` You will notice `data-sanity` attributes being added to the wrapping and individual tags of the list. As well as a useOptimistic hook to apply these changes in the UI immediately, while the mutation in the content lake is still happening. 1. **Update** the `Post` component to include the `RelatedPosts` component. ```tsx:src/components/post.tsx import Image from "next/image"; import { PortableText } from "next-sanity"; import { Author } from "@/components/author"; import { Categories } from "@/components/categories"; import { components } from "@/sanity/portableTextComponents"; import { POST_QUERYResult } from "@/sanity/types"; import { PublishedAt } from "@/components/published-at"; import { Title } from "@/components/title"; import { urlFor } from "@/sanity/lib/image"; import { RelatedPosts } from "@/components/related-posts"; export function Post(props: NonNullable) { const { _id, title, author, mainImage, body, publishedAt, categories, relatedPosts, } = props; return (
{title}
{mainImage ? (
) : null} {body ? (
) : null}
); } ``` Add a few Related Posts to any post document. Now within Presentation, you should be able to drag-and-drop to reorder their position, and see the content change in the Studio.