# Drag and drop in Visual Editing https://www.sanity.io/learn/course/page-building/drag-and-drop-in-visual-editing.md Allow authors to re-order blocks on page, without editing the document. The same functionality you setup in [Add drag-and-drop elements](https://www.sanity.io/learn/course/visual-editing-with-next-js/add-drag-and-drop-elements) can be used here for your Page Builder array. This way authors can reorder array items on the page without needing to use the document editor. 1. You can setup drag-and-drop for _any_ array type field. Consider adding it to the Features and FAQs blocks as well. ## Adding drag handles Drag-and-drop support in Presentation requires the outer DOM element of an array—and every DOM element for an item within the array—to contain additional `data-sanity` attributes. These attributes are created with a `createDataAttribute` function exported from `next-sanity` and require the ID and Type of the source document. Additionally, for fast on-page changes, a `useOptimistic` hook is provided by `next-sanity`. Using this hook will require changing to a client component. The `PageBuilder` component you created in a previous lesson is where we can create and set these attributes, for the `content` array and its individual blocks. 1. **Update** your `PageBuilder` component to add attributes for drag-and-drop ```tsx:src/components/page-builder.tsx "use client"; import { Hero } from "@/components/blocks/hero"; import { Features } from "@/components/blocks/features"; import { SplitImage } from "@/components/blocks/split-image"; import { FAQs } from "@/components/blocks/faqs"; import { PAGE_QUERYResult } from "@/sanity/types"; import { client } from "@/sanity/lib/client"; import { createDataAttribute } from "next-sanity"; import { useOptimistic } from "next-sanity/hooks"; type PageBuilderProps = { content: NonNullable["content"]; documentId: string; documentType: string; }; const { projectId, dataset, stega } = client.config(); export const createDataAttributeConfig = { projectId, dataset, baseUrl: typeof stega.studioUrl === "string" ? stega.studioUrl : "", }; export function PageBuilder({ content, documentId, documentType, }: PageBuilderProps) { const blocks = useOptimistic< NonNullable["content"] | undefined, NonNullable >(content, (state, action) => { if (action.id === documentId) { return action?.document?.content?.map( (block) => state?.find((s) => s._key === block?._key) || block ); } return state; }); if (!Array.isArray(blocks)) { return null; } return (
{blocks.map((block) => { const DragHandle = ({ children }: { children: React.ReactNode }) => (
{children}
); switch (block._type) { case "hero": return ( ); case "features": return ( ); case "splitImage": return ( ); case "faqs": return ( ); default: // This is a fallback for when we don't have a block type return
Block not found: {block._type}
; } })}
); } ``` The `PageBuilder` component now requires the source document ID and document type. 1. **Update** your routes that load this component to include these props. ```tsx:src/app/(frontend)/[slug]/page.tsx import { PageBuilder } from "@/components/PageBuilder"; import { sanityFetch } from "@/sanity/lib/live"; import { PAGE_QUERY } from "@/sanity/lib/queries"; export default async function Page({ params, }: { params: Promise<{ slug: string }>; }) { const { data: page } = await sanityFetch({ query: PAGE_QUERY, params: await params, }); return page?.content ? ( ) : null; } ``` Don't forget the home page route as well ```tsx:src/app/(frontend)/page.tsx import { PageBuilder } from "@/components/PageBuilder"; import { sanityFetch } from "@/sanity/lib/live"; import { HOME_PAGE_QUERY } from "@/sanity/lib/queries"; export default async function Page() { const { data: page } = await sanityFetch({ query: HOME_PAGE_QUERY, }); return page?.homePage?.content ? ( ) : null; } ``` ### Test it out Within Presentation you should now see the "drag handle" icon (two columns of three dots) when hovering over the outer edge of each block. Click and hold, to drag-and-drop. Additionally, hold shift while dragging to zoom the page out and see the entire array at once. You've created your page builder, wired it up to work on the frontend, and used the visual editor to rearrange block order. You have built the gold standard of editorial experience for your end users. Great job! ## Time to review What remains is to learn about some of the pitfalls and challenges of using the visual editor at scale, which will be covered in the final lesson.