CINXE.COM
Localization | Documentation | Payload
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" href="/_next/static/media/096fc0a920206bee-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/0a519dad0c4c2790-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/32d694b729e49e86-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/5566c67f1508ae8a-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/66f30814ff6d7cdf.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/716743f5eba8aa5c-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/77e7188387b00f5d-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/bb521019c7c7526d-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/c86d5e148479770d-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/d8aa0c7d96365198-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="preload" href="/_next/static/media/fbc69893487b1118-s.p.woff2" as="font" crossorigin="" type="font/woff2"/><link rel="stylesheet" href="/_next/static/css/390d0663ad838fdc.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/26afb2933518d3bd.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/e7ca63aea2683535.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/fdb2722c82ebf619.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/14c8222b4e6bfb9e.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/f574d745d6edfb67.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/b1cf855db6ac51b1.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/ec76de1699f3bb1c.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/ca44753475331c48.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/e4938badb52137ae.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/3335bd7359ecbb8a.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/26e2d81bd32c127c.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/fe14b9b58f5d10c1.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/972e81b151cbc342.css" data-precedence="next"/><link rel="stylesheet" href="/_next/static/css/add6d6451a368571.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-7fa974e68fcbf6b9.js"/><script src="/_next/static/chunks/79823dc8-6d7c06352f390c6b.js" async=""></script><script src="/_next/static/chunks/1195-8239fc7777de9447.js" async=""></script><script src="/_next/static/chunks/main-app-3313e6498c9f96e0.js" async=""></script><script src="/_next/static/chunks/app/global-error-f07b6b99d52dc53c.js" async=""></script><script src="/_next/static/chunks/6658-fb794c29f5a98524.js" async=""></script><script src="/_next/static/chunks/3966-a2a2bbc25ead1892.js" async=""></script><script src="/_next/static/chunks/4800-03b4f9e37b2b6c75.js" async=""></script><script src="/_next/static/chunks/7103-67c634765b48b90f.js" async=""></script><script src="/_next/static/chunks/6137-3a61cc21a88452a0.js" async=""></script><script src="/_next/static/chunks/6873-4a30b3dc38bee630.js" async=""></script><script src="/_next/static/chunks/app/(frontend)/layout-7e0ce4c8601538b2.js" async=""></script><script src="/_next/static/chunks/app/(frontend)/error-93d8997970acc7c2.js" async=""></script><script src="/_next/static/chunks/8640-19363ff25b5bc926.js" async=""></script><script src="/_next/static/chunks/6856-746769b4a7fc3a5e.js" async=""></script><script src="/_next/static/chunks/9393-f5cd841433221a01.js" async=""></script><script src="/_next/static/chunks/8052-dbdea2ef46226629.js" async=""></script><script src="/_next/static/chunks/730-f59bf88ee3e8bdc4.js" async=""></script><script src="/_next/static/chunks/4139-fca05c995471e50e.js" async=""></script><script src="/_next/static/chunks/6579-b0979af465cec810.js" async=""></script><script src="/_next/static/chunks/app/(frontend)/(pages)/layout-aa781834d55c1303.js" async=""></script><script src="/_next/static/chunks/app/(frontend)/not-found-80590df17b7d6f8e.js" async=""></script><script src="/_next/static/chunks/4615-52c1ec00e8f8c199.js" async=""></script><script src="/_next/static/chunks/2033-d91a980ce0ad8789.js" async=""></script><script src="/_next/static/chunks/5179-acc9e6e828caa86a.js" async=""></script><script src="/_next/static/chunks/8819-d79276df4be00a63.js" async=""></script><script src="/_next/static/chunks/1092-1aacfd2ddddca0f7.js" async=""></script><script src="/_next/static/chunks/5363-255b3f37e9cb9dca.js" async=""></script><script src="/_next/static/chunks/6429-416b9abc3627a940.js" async=""></script><script src="/_next/static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js" async=""></script><link rel="preload" href="/_next/static/chunks/3469.2ed73089f6076d50.js" as="script" fetchPriority="low"/><link rel="preload" href="/_next/static/chunks/2657.413390c29f0acedd.js" as="script" fetchPriority="low"/><meta name="next-size-adjust"/><link rel="icon" href="/images/favicon.svg"/><link rel="dns-prefetch" href="https://cloud-api.payloadcms.com"/><link rel="dns-prefetch" href="https://api.github.com/repos/payloadcms/payload"/><link rel="preconnect" href="https://www.googletagmanager.com"/><link rel="preconnect" href="https://www.google-analytics.com"/><title>Localization | Documentation | Payload</title><meta name="description" content="Add and maintain as many locales as you need by adding Localization to your Payload Config, set options for default locale, fallbacks, fields and more."/><meta property="og:title" content="Localization | Documentation | Payload"/><meta property="og:description" content="Payload is a headless CMS and application framework built with TypeScript, Node.js, React and MongoDB"/><meta property="og:url" content="https://payloadcms.com/docs/configuration/localization"/><meta property="og:site_name" content="Payload"/><meta property="og:image" content="https://payloadcms.com/api/og?topic=configuration&title=Localization"/><meta property="og:type" content="website"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:creator" content="@payloadcms"/><meta name="twitter:title" content="Localization | Documentation | Payload"/><meta name="twitter:description" content="Payload is a headless CMS and application framework built with TypeScript, Node.js, React and MongoDB"/><meta name="twitter:image" content="https://payloadcms.com/api/og?topic=configuration&title=Localization"/><link rel="apple-touch-icon" href="/apple-icon.png?1be71cc371876318" type="image/png" sizes="180x180"/><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@docsearch/css@3"/><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body class="__variable_c1e5c9 __variable_b25fb2"><style type="text/css">.react-css-grid__grid { display: grid; column-gap: 3rem; row-gap: 4rem; }.react-css-grid__grid--xl-cols-1 { grid-template-columns: repeat(1, 1fr); }.react-css-grid__grid--xl-cols-2 { grid-template-columns: repeat(2, 1fr); }.react-css-grid__grid--xl-cols-3 { grid-template-columns: repeat(3, 1fr); }.react-css-grid__grid--xl-cols-4 { grid-template-columns: repeat(4, 1fr); }.react-css-grid__grid--xl-cols-5 { grid-template-columns: repeat(5, 1fr); }.react-css-grid__grid--xl-cols-6 { grid-template-columns: repeat(6, 1fr); }.react-css-grid__grid--xl-cols-7 { grid-template-columns: repeat(7, 1fr); }.react-css-grid__grid--xl-cols-8 { grid-template-columns: repeat(8, 1fr); }.react-css-grid__grid--xl-cols-9 { grid-template-columns: repeat(9, 1fr); }.react-css-grid__grid--xl-cols-10 { grid-template-columns: repeat(10, 1fr); }.react-css-grid__grid--xl-cols-11 { grid-template-columns: repeat(11, 1fr); }.react-css-grid__grid--xl-cols-12 { grid-template-columns: repeat(12, 1fr); }.react-css-grid__cell--xl-col-start-1 { grid-column-start: 1; }.react-css-grid__cell--xl-col-start-2 { grid-column-start: 2; }.react-css-grid__cell--xl-col-start-3 { grid-column-start: 3; }.react-css-grid__cell--xl-col-start-4 { grid-column-start: 4; }.react-css-grid__cell--xl-col-start-5 { grid-column-start: 5; }.react-css-grid__cell--xl-col-start-6 { grid-column-start: 6; }.react-css-grid__cell--xl-col-start-7 { grid-column-start: 7; }.react-css-grid__cell--xl-col-start-8 { grid-column-start: 8; }.react-css-grid__cell--xl-col-start-9 { grid-column-start: 9; }.react-css-grid__cell--xl-col-start-10 { grid-column-start: 10; }.react-css-grid__cell--xl-col-start-11 { grid-column-start: 11; }.react-css-grid__cell--xl-col-start-12 { grid-column-start: 12; }.react-css-grid__cell--xl-col-end-1 { grid-column-end: span 1; }.react-css-grid__cell--xl-col-end-2 { grid-column-end: span 2; }.react-css-grid__cell--xl-col-end-3 { grid-column-end: span 3; }.react-css-grid__cell--xl-col-end-4 { grid-column-end: span 4; }.react-css-grid__cell--xl-col-end-5 { grid-column-end: span 5; }.react-css-grid__cell--xl-col-end-6 { grid-column-end: span 6; }.react-css-grid__cell--xl-col-end-7 { grid-column-end: span 7; }.react-css-grid__cell--xl-col-end-8 { grid-column-end: span 8; }.react-css-grid__cell--xl-col-end-9 { grid-column-end: span 9; }.react-css-grid__cell--xl-col-end-10 { grid-column-end: span 10; }.react-css-grid__cell--xl-col-end-11 { grid-column-end: span 11; }.react-css-grid__cell--xl-col-end-12 { grid-column-end: span 12; } @media (max-width: 1680px) { .react-css-grid__grid { display: grid; column-gap: 2rem; row-gap: 2rem; } .react-css-grid__grid--l-cols-1 { grid-template-columns: repeat(1, 1fr); }.react-css-grid__grid--l-cols-2 { grid-template-columns: repeat(2, 1fr); }.react-css-grid__grid--l-cols-3 { grid-template-columns: repeat(3, 1fr); }.react-css-grid__grid--l-cols-4 { grid-template-columns: repeat(4, 1fr); }.react-css-grid__grid--l-cols-5 { grid-template-columns: repeat(5, 1fr); }.react-css-grid__grid--l-cols-6 { grid-template-columns: repeat(6, 1fr); }.react-css-grid__grid--l-cols-7 { grid-template-columns: repeat(7, 1fr); }.react-css-grid__grid--l-cols-8 { grid-template-columns: repeat(8, 1fr); }.react-css-grid__grid--l-cols-9 { grid-template-columns: repeat(9, 1fr); }.react-css-grid__grid--l-cols-10 { grid-template-columns: repeat(10, 1fr); }.react-css-grid__grid--l-cols-11 { grid-template-columns: repeat(11, 1fr); }.react-css-grid__grid--l-cols-12 { grid-template-columns: repeat(12, 1fr); } .react-css-grid__cell--l-col-start-1 { grid-column-start: 1; }.react-css-grid__cell--l-col-start-2 { grid-column-start: 2; }.react-css-grid__cell--l-col-start-3 { grid-column-start: 3; }.react-css-grid__cell--l-col-start-4 { grid-column-start: 4; }.react-css-grid__cell--l-col-start-5 { grid-column-start: 5; }.react-css-grid__cell--l-col-start-6 { grid-column-start: 6; }.react-css-grid__cell--l-col-start-7 { grid-column-start: 7; }.react-css-grid__cell--l-col-start-8 { grid-column-start: 8; }.react-css-grid__cell--l-col-start-9 { grid-column-start: 9; }.react-css-grid__cell--l-col-start-10 { grid-column-start: 10; }.react-css-grid__cell--l-col-start-11 { grid-column-start: 11; }.react-css-grid__cell--l-col-start-12 { grid-column-start: 12; } .react-css-grid__cell--l-col-end-1 { grid-column-end: span 1; }.react-css-grid__cell--l-col-end-2 { grid-column-end: span 2; }.react-css-grid__cell--l-col-end-3 { grid-column-end: span 3; }.react-css-grid__cell--l-col-end-4 { grid-column-end: span 4; }.react-css-grid__cell--l-col-end-5 { grid-column-end: span 5; }.react-css-grid__cell--l-col-end-6 { grid-column-end: span 6; }.react-css-grid__cell--l-col-end-7 { grid-column-end: span 7; }.react-css-grid__cell--l-col-end-8 { grid-column-end: span 8; }.react-css-grid__cell--l-col-end-9 { grid-column-end: span 9; }.react-css-grid__cell--l-col-end-10 { grid-column-end: span 10; }.react-css-grid__cell--l-col-end-11 { grid-column-end: span 11; }.react-css-grid__cell--l-col-end-12 { grid-column-end: span 12; } } @media (max-width: 1024px) { .react-css-grid__grid { display: grid; column-gap: 2rem; row-gap: 1rem; } .react-css-grid__grid--m-cols-1 { grid-template-columns: repeat(1, 1fr); }.react-css-grid__grid--m-cols-2 { grid-template-columns: repeat(2, 1fr); }.react-css-grid__grid--m-cols-3 { grid-template-columns: repeat(3, 1fr); }.react-css-grid__grid--m-cols-4 { grid-template-columns: repeat(4, 1fr); }.react-css-grid__grid--m-cols-5 { grid-template-columns: repeat(5, 1fr); }.react-css-grid__grid--m-cols-6 { grid-template-columns: repeat(6, 1fr); }.react-css-grid__grid--m-cols-7 { grid-template-columns: repeat(7, 1fr); }.react-css-grid__grid--m-cols-8 { grid-template-columns: repeat(8, 1fr); } .react-css-grid__cell--m-col-start-1 { grid-column-start: 1; }.react-css-grid__cell--m-col-start-2 { grid-column-start: 2; }.react-css-grid__cell--m-col-start-3 { grid-column-start: 3; }.react-css-grid__cell--m-col-start-4 { grid-column-start: 4; }.react-css-grid__cell--m-col-start-5 { grid-column-start: 5; }.react-css-grid__cell--m-col-start-6 { grid-column-start: 6; }.react-css-grid__cell--m-col-start-7 { grid-column-start: 7; }.react-css-grid__cell--m-col-start-8 { grid-column-start: 8; } .react-css-grid__cell--m-col-end-1 { grid-column-end: span 1; }.react-css-grid__cell--m-col-end-2 { grid-column-end: span 2; }.react-css-grid__cell--m-col-end-3 { grid-column-end: span 3; }.react-css-grid__cell--m-col-end-4 { grid-column-end: span 4; }.react-css-grid__cell--m-col-end-5 { grid-column-end: span 5; }.react-css-grid__cell--m-col-end-6 { grid-column-end: span 6; }.react-css-grid__cell--m-col-end-7 { grid-column-end: span 7; }.react-css-grid__cell--m-col-end-8 { grid-column-end: span 8; } } @media (max-width: 768px) { .react-css-grid__grid { display: grid; column-gap: 1rem; row-gap: 1rem; } .react-css-grid__grid--s-cols-1 { grid-template-columns: repeat(1, 1fr); }.react-css-grid__grid--s-cols-2 { grid-template-columns: repeat(2, 1fr); }.react-css-grid__grid--s-cols-3 { grid-template-columns: repeat(3, 1fr); }.react-css-grid__grid--s-cols-4 { grid-template-columns: repeat(4, 1fr); }.react-css-grid__grid--s-cols-5 { grid-template-columns: repeat(5, 1fr); }.react-css-grid__grid--s-cols-6 { grid-template-columns: repeat(6, 1fr); }.react-css-grid__grid--s-cols-7 { grid-template-columns: repeat(7, 1fr); }.react-css-grid__grid--s-cols-8 { grid-template-columns: repeat(8, 1fr); } .react-css-grid__cell--s-col-start-1 { grid-column-start: 1; }.react-css-grid__cell--s-col-start-2 { grid-column-start: 2; }.react-css-grid__cell--s-col-start-3 { grid-column-start: 3; }.react-css-grid__cell--s-col-start-4 { grid-column-start: 4; }.react-css-grid__cell--s-col-start-5 { grid-column-start: 5; }.react-css-grid__cell--s-col-start-6 { grid-column-start: 6; }.react-css-grid__cell--s-col-start-7 { grid-column-start: 7; }.react-css-grid__cell--s-col-start-8 { grid-column-start: 8; } .react-css-grid__cell--s-col-end-1 { grid-column-end: span 1; }.react-css-grid__cell--s-col-end-2 { grid-column-end: span 2; }.react-css-grid__cell--s-col-end-3 { grid-column-end: span 3; }.react-css-grid__cell--s-col-end-4 { grid-column-end: span 4; }.react-css-grid__cell--s-col-end-5 { grid-column-end: span 5; }.react-css-grid__cell--s-col-end-6 { grid-column-end: span 6; }.react-css-grid__cell--s-col-end-7 { grid-column-end: span 7; }.react-css-grid__cell--s-col-end-8 { grid-column-end: span 8; } } .react-css-grid__cell { min-width: 0; } </style><style></style><div><div><header class="Header_header__qBw1J Header_hideBackground__CZ0fh"><div class="DesktopNav_desktopNav__lsf_S" style="width:100%"><div class="DesktopNav_desktopNav__lsf_S Gutter_leftGutter__UWO6t Gutter_rightGutter__qUzUU"><div class="DesktopNav_grid__q5Akf grid"><div class="DesktopNav_logo__Tzy9w cols-4"><a class="DesktopNav_logo__Tzy9w" aria-label="Full Payload Logo" href="/"><svg class="w-auto h-[30px]" height="44" id="b" viewBox="0 0 193.38 43.5" width="194" xmlns="http://www.w3.org/2000/svg"><g id="c"><path d="M18.01,35.63l-12.36-7.13c-.15-.09-.25-.25-.25-.43v-11.02c0-.19.21-.31.37-.22l14.35,8.28c.2.12.45-.03.45-.26v-5.37c0-.21-.11-.41-.3-.52L3.01,9c-.15-.09-.35-.09-.5,0l-2.26,1.31c-.15.09-.25.25-.25.43v20.47c0,.18.1.34.25.43l17.73,10.24c.15.09.35.09.5,0l14.89-8.6c.2-.12.2-.4,0-.52l-4.64-2.68c-.19-.11-.41-.11-.6,0l-9.61,5.55c-.15.09-.35.09-.5,0Z" fill="currentColor"></path><path d="M36.21,10.3L18.48.07c-.15-.09-.35-.09-.5,0l-9.37,5.41c-.2.12-.2.4,0,.52l4.6,2.66c.19.11.41.11.6,0l4.2-2.42c.15-.09.35-.09.5,0l12.36,7.13c.15.09.25.25.25.43v11.07c0,.21.11.41.3.52l4.6,2.65c.2.12.45-.03.45-.26V10.74c0-.18-.1-.34-.25-.43Z" fill="currentColor"></path><g id="d"><path d="M193.38,9.47c0,1.94-1.48,3.32-3.3,3.32s-3.31-1.39-3.31-3.32,1.49-3.31,3.31-3.31,3.3,1.39,3.3,3.31ZM192.92,9.47c0-1.68-1.26-2.88-2.84-2.88s-2.84,1.2-2.84,2.88,1.26,2.89,2.84,2.89,2.84-1.2,2.84-2.89ZM188.69,11.17v-3.51h1.61c.85,0,1.35.39,1.35,1.15,0,.53-.3.86-.67,1.02l.79,1.35h-.89l-.72-1.22h-.64v1.22h-.82ZM190.18,9.31c.46,0,.64-.16.64-.5s-.19-.49-.64-.49h-.67v.99h.67Z" fill="currentColor"></path><path d="M54.72,24.84v10.93h-5.4V6.1h12.26c7.02,0,11.1,3.2,11.1,9.39s-4.07,9.35-11.06,9.35h-6.9,0ZM61.12,20.52c4.07,0,6.11-1.66,6.11-5.03s-2.04-5.03-6.11-5.03h-6.4v10.06h6.4Z" fill="currentColor"></path><path d="M85.94,32.45c-1,2.41-3.66,3.78-7.02,3.78-4.11,0-7.11-2.29-7.11-6.11,0-4.24,3.32-5.98,7.61-6.48l6.32-.71v-1c0-2.58-1.58-3.82-3.99-3.82s-3.74,1.29-3.91,3.24h-5.11c.46-4.53,3.99-7.19,9.18-7.19,5.74,0,9.02,2.7,9.02,8.19v8.15c0,1.95.08,3.58.42,5.28h-5.11c-.21-1.16-.29-2.29-.29-3.32h0ZM85.73,27.58v-1.29l-4.7.54c-2.24.29-3.95.79-3.95,2.99,0,1.66,1.16,2.7,3.28,2.7,2.74,0,5.36-1.62,5.36-4.95h0Z" fill="currentColor"></path><path d="M90.39,14.66h5.4l5.86,15.92h.08l5.57-15.92h5.28l-8.23,21.49c-2,5.28-4.45,7.32-8.89,7.36-.71,0-1.7-.08-2.45-.21v-4.03c.62.13.96.13,1.41.13,2.16,0,3.07-.75,4.2-3.66l-8.23-21.07h0Z" fill="currentColor"></path><path d="M113.46,35.77V6.1h5.32v29.67h-5.32Z" fill="currentColor"></path><path d="M130.79,36.27c-6.23,0-10.68-4.2-10.68-11.05s4.45-11.05,10.68-11.05,10.68,4.24,10.68,11.05-4.45,11.05-10.68,11.05ZM130.79,32.32c3.41,0,5.36-2.66,5.36-7.11s-1.95-7.11-5.36-7.11-5.36,2.7-5.36,7.11,1.91,7.11,5.36,7.11Z" fill="currentColor"></path><path d="M156.19,32.45c-1,2.41-3.66,3.78-7.02,3.78-4.11,0-7.11-2.29-7.11-6.11,0-4.24,3.32-5.98,7.61-6.48l6.32-.71v-1c0-2.58-1.58-3.82-3.99-3.82s-3.74,1.29-3.91,3.24h-5.11c.46-4.53,3.99-7.19,9.19-7.19,5.74,0,9.02,2.7,9.02,8.19v8.15c0,1.95.08,3.58.42,5.28h-5.11c-.21-1.16-.29-2.29-.29-3.32h0ZM155.98,27.58v-1.29l-4.7.54c-2.24.29-3.95.79-3.95,2.99,0,1.66,1.16,2.7,3.28,2.7,2.74,0,5.36-1.62,5.36-4.95h0Z" fill="currentColor"></path><path d="M178.5,32.41c-1.04,2.12-3.58,3.87-6.78,3.87-5.53,0-9.31-4.49-9.31-11.05s3.78-11.05,9.31-11.05c3.28,0,5.69,1.83,6.69,3.95V6.1h5.32v29.67h-5.24v-3.37h0ZM178.55,24.84c0-4.11-1.95-6.78-5.32-6.78s-5.45,2.83-5.45,7.15,2,7.15,5.45,7.15,5.32-2.66,5.32-6.78v-.75h0Z" fill="currentColor"></path></g></g></svg></a></div><div class="DesktopNav_content__9r3EV cols-8"><div class="DesktopNav_tabs__KG3JB"><div><button class="DesktopNav_tab__0rFZ8"><a class="DesktopNav_directLink__TIgn3" id="" href="/use-cases">Product</a></button><div class="grid DesktopNav_dropdown__brZO3"><div class="DesktopNav_description__O1eBY cols-4">Simplify your stack and build anything. Or everything.<div class="DesktopNav_descriptionLinks__KApPo"><a class="DesktopNav_descriptionLink__62YgK" id="" href="/talk-to-us">Schedule a Demo<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a></div></div><div class="cols-3 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_linkList__BlDef"><div class="DesktopNav_listLabel__k2vVd">USE CASES</div><a class="DesktopNav_link__BUqGx" id="" href="/use-cases/headless-cms">Headless CMS</a><a class="DesktopNav_link__BUqGx" id="" href="/use-cases/enterprise-app-builder">Enterprise App Builder</a><a class="DesktopNav_link__BUqGx" id="" href="/use-cases/headless-ecommerce">Headless E-Commerce</a><a class="DesktopNav_link__BUqGx" id="" href="/use-cases/digital-asset-management">Digital Asset Management</a></div></div><div class="cols-3 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_linkList__BlDef"><div class="DesktopNav_listLabel__k2vVd">FEATURES</div><a class="DesktopNav_link__BUqGx" id="" href="/multi-tenancy">Multi-Tenancy</a><a class="DesktopNav_link__BUqGx" id="" href="/white-label-cms-admin-panel">White Label</a><a class="DesktopNav_link__BUqGx" id="" href="/localization">Localization</a><a class="DesktopNav_link__BUqGx" id="" href="/access-control">Access Control</a><a class="DesktopNav_link__BUqGx" id="" href="/headless-cms-auth">Auth</a></div></div><div class="cols-6 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_featuredLink__IvcPS"><div class="DesktopNav_listLabel__k2vVd">CASE STUDIES</div><div class="RichText_richText__uefD_"><h4>See what others are building with Payload.</h4></div><div class="DesktopNav_featuredLinkWrap__mViVy"><a class="DesktopNav_featuredLinks__qvVWE" id="" href="/case-studies">Browse Case Studies<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a></div></div></div></div></div><div><button class="DesktopNav_tab__0rFZ8">Why Payload</button><div class="grid DesktopNav_dropdown__brZO3"><div class="DesktopNav_description__O1eBY cols-4">Build tomorrow’s web with a modern solution you truly own.<div class="DesktopNav_descriptionLinks__KApPo"></div></div><div class="cols-3 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_linkList__BlDef"><div class="DesktopNav_listLabel__k2vVd">PAYLOAD IS FOR</div><a class="DesktopNav_link__BUqGx" id="" href="/developers">Developers</a><a class="DesktopNav_link__BUqGx" id="" href="/marketers">Marketing teams</a><a class="DesktopNav_link__BUqGx" id="" href="/enterprise">Enterprise companies</a><a class="DesktopNav_link__BUqGx" id="" href="/become-a-partner">Agencies & Consultancies</a></div></div><div class="cols-3 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_linkList__BlDef"><div class="DesktopNav_listLabel__k2vVd">COMPARE PAYLOAD</div><a class="DesktopNav_link__BUqGx" id="" href="/compare/wordpress">Payload vs WordPress</a><a class="DesktopNav_link__BUqGx" id="" href="/compare/contentful">Payload vs Contentful</a><a class="DesktopNav_link__BUqGx" id="" href="/compare/sanity">Payload vs Sanity</a><a class="DesktopNav_link__BUqGx" id="" href="/compare/strapi">Payload vs Strapi</a><a class="DesktopNav_link__BUqGx" id="" href="/compare/directus">Payload vs Directus</a></div></div><div class="cols-6 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_featuredLink__IvcPS"><div class="DesktopNav_listLabel__k2vVd">AGENCY TESTIMONIAL</div><div class="RichText_richText__uefD_"><h4>"Payload has transformed the way our clients manage content. It's an indispensable tool for any modern agency."</h4></div><div class="DesktopNav_featuredLinkWrap__mViVy"><a class="DesktopNav_featuredLinks__qvVWE" id="" href="/become-a-partner">Become a Partner<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><a class="DesktopNav_featuredLinks__qvVWE" id="" href="/partners">Find a Partner<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a></div></div></div></div></div><div><button class="DesktopNav_tab__0rFZ8"><a class="DesktopNav_directLink__TIgn3" id="" href="/developers">Developers</a></button><div class="grid DesktopNav_dropdown__brZO3"><div class="DesktopNav_description__O1eBY cols-4">Code-based nature means you can build on top of it to power anything.<div class="DesktopNav_descriptionLinks__KApPo"></div></div><div class="cols-3 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_linkList__BlDef"><div class="DesktopNav_listLabel__k2vVd">Resources</div><a class="DesktopNav_link__BUqGx" id="" href="/docs">Documentation</a><a href="https://github.com/payloadcms/payload/tree/main/examples" target="_blank" rel="noopener noreferrer" class="DesktopNav_link__BUqGx" id="">Examples<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><a href="https://github.com/payloadcms/payload/tree/main/templates" target="_blank" rel="noopener noreferrer" class="DesktopNav_link__BUqGx" id="">Templates<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><a href="https://github.com/payloadcms/payload" target="_blank" rel="noopener noreferrer" class="DesktopNav_link__BUqGx" id="">GitHub<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><a href="https://github.com/payloadcms/payload/releases" target="_blank" rel="noopener noreferrer" class="DesktopNav_link__BUqGx" id="">Releases<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><a class="DesktopNav_link__BUqGx" id="" href="/blog">Blog</a></div></div><div class="cols-3 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_linkList__BlDef"><div class="DesktopNav_listLabel__k2vVd">Community</div><a href="https://github.com/payloadcms/payload/discussions/categories/roadmap" target="_blank" rel="noopener noreferrer" class="DesktopNav_link__BUqGx" id="">Roadmap<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><a href="https://discord.com/invite/r6sCXqVk3v" target="_blank" rel="noopener noreferrer" class="DesktopNav_link__BUqGx" id="">Discord<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><a class="DesktopNav_link__BUqGx" id="" href="/community-help">Community Help</a></div></div><div class="cols-6 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_featuredLink__IvcPS"><div class="DesktopNav_listLabel__k2vVd">Payload Cloud</div><div class="RichText_richText__uefD_"><h4>Deploy your entire stack in one place with Payload Cloud.</h4></div><div class="DesktopNav_featuredLinkWrap__mViVy"><a class="DesktopNav_featuredLinks__qvVWE" id="" href="/login">Login<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><a class="DesktopNav_featuredLinks__qvVWE" id="" href="/cloud-pricing">Cloud Pricing<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a></div></div></div></div></div><div><button class="DesktopNav_tab__0rFZ8"><a class="DesktopNav_directLink__TIgn3" id="" href="/enterprise">Enterprise</a></button><div class="grid DesktopNav_dropdown__brZO3"><div class="DesktopNav_description__O1eBY cols-4">It’s time to take back your content infrastructure.<div class="DesktopNav_descriptionLinks__KApPo"><a class="DesktopNav_descriptionLink__62YgK" id="" href="/talk-to-us">Schedule a Demo<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a></div></div><div class="cols-3 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_linkList__BlDef"><div class="DesktopNav_listLabel__k2vVd">Enterprise Features</div><a class="DesktopNav_link__BUqGx" id="" href="/enterprise/single-sign-on-sso">SSO</a><a class="DesktopNav_link__BUqGx" id="" href="/enterprise/ai-framework">AI Auto-Embedding</a><a class="DesktopNav_link__BUqGx" id="" href="/enterprise/publishing-workflows">Publishing Workflows</a><a class="DesktopNav_link__BUqGx" id="" href="/enterprise/visual-editor">Visual Editor</a><a class="DesktopNav_link__BUqGx" id="" href="/enterprise/headless-ab-variant-testing">Static A/B testing</a><a class="DesktopNav_link__BUqGx" id="" href="/enterprise/enterprise-ai">AI features</a></div></div><div class="cols-3 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_linkList__BlDef"><div class="DesktopNav_listLabel__k2vVd">Customer Stories</div><a class="DesktopNav_link__BUqGx" id="" href="/case-studies/microsoft">Microsoft</a><a class="DesktopNav_link__BUqGx" id="" href="/case-studies/blue-origin-club-for-the-future">Blue Origin</a><a class="DesktopNav_link__BUqGx" id="" href="/case-studies/hello-bello">Hello Bello</a><a class="DesktopNav_link__BUqGx" id="" href="/case-studies/mythical-society">Mythical Society</a><a class="DesktopNav_link__BUqGx" id="" href="/case-studies/tekton">Tekton</a></div></div><div class="cols-6 DesktopNav_dropdownItem__FkeEn false"><div class="DesktopNav_featuredLink__IvcPS"><div class="DesktopNav_listLabel__k2vVd">Featured Customer Story</div><div class="RichText_richText__uefD_"><h4>Microsoft chose Payload to tell the world about AI.</h4></div><div class="DesktopNav_featuredLinkWrap__mViVy"><a class="DesktopNav_featuredLinks__qvVWE" id="" href="/case-studies/microsoft">Read the case study<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DesktopNav_linkArrow__VbaiX icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a></div></div></div></div></div><div><button class="DesktopNav_tab__0rFZ8"><a class="DesktopNav_directLink__TIgn3" id="" href="/docs">Docs</a></button></div><div class="DesktopNav_underline__Kv7In" style="opacity:0" aria-hidden="true"><div class="DesktopNav_underlineFill__72ueo"></div></div></div></div><div class="cols-4"><div class="DesktopNav_secondaryNavItems__k5y7V false"><a class="DesktopNav_github__bncNA" href="https://github.com/payloadcms/payload" target="_blank" rel="noreferrer" aria-label="Payload's GitHub"><svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 96"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="currentColor"></path></svg></a><a href="/login">Login</a><a class="DesktopNav_button__fBFQs" id="" href="/get-started">Get Started</a><div class="Docsearch_docSearch__tCi81"><!--$--><button type="button" class="DocSearch DocSearch-Button" aria-label="Search (Command+K)"><span class="DocSearch-Button-Container"><svg width="20" height="20" class="DocSearch-Search-Icon" viewBox="0 0 20 20" aria-hidden="true"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"></span></button><!--/$--></div></div></div></div><div class="DesktopNav_background__0OEVt" style="height:0px;top:0px"></div></div></div><div class="MobileNav_mobileNav__oaRvV"><div><div class="Gutter_leftGutter__UWO6t Gutter_rightGutter__qUzUU"><div class="grid"><div class="MobileNav_menuBarContainer__B_PUc cols-16 cols-m-8"><a class="MobileNav_logo__wliSL" aria-label="Full Payload Logo" href="/"><svg class="w-auto h-[30px]" height="44" id="b" viewBox="0 0 193.38 43.5" width="194" xmlns="http://www.w3.org/2000/svg"><g id="c"><path d="M18.01,35.63l-12.36-7.13c-.15-.09-.25-.25-.25-.43v-11.02c0-.19.21-.31.37-.22l14.35,8.28c.2.12.45-.03.45-.26v-5.37c0-.21-.11-.41-.3-.52L3.01,9c-.15-.09-.35-.09-.5,0l-2.26,1.31c-.15.09-.25.25-.25.43v20.47c0,.18.1.34.25.43l17.73,10.24c.15.09.35.09.5,0l14.89-8.6c.2-.12.2-.4,0-.52l-4.64-2.68c-.19-.11-.41-.11-.6,0l-9.61,5.55c-.15.09-.35.09-.5,0Z" fill="currentColor"></path><path d="M36.21,10.3L18.48.07c-.15-.09-.35-.09-.5,0l-9.37,5.41c-.2.12-.2.4,0,.52l4.6,2.66c.19.11.41.11.6,0l4.2-2.42c.15-.09.35-.09.5,0l12.36,7.13c.15.09.25.25.25.43v11.07c0,.21.11.41.3.52l4.6,2.65c.2.12.45-.03.45-.26V10.74c0-.18-.1-.34-.25-.43Z" fill="currentColor"></path><g id="d"><path d="M193.38,9.47c0,1.94-1.48,3.32-3.3,3.32s-3.31-1.39-3.31-3.32,1.49-3.31,3.31-3.31,3.3,1.39,3.3,3.31ZM192.92,9.47c0-1.68-1.26-2.88-2.84-2.88s-2.84,1.2-2.84,2.88,1.26,2.89,2.84,2.89,2.84-1.2,2.84-2.89ZM188.69,11.17v-3.51h1.61c.85,0,1.35.39,1.35,1.15,0,.53-.3.86-.67,1.02l.79,1.35h-.89l-.72-1.22h-.64v1.22h-.82ZM190.18,9.31c.46,0,.64-.16.64-.5s-.19-.49-.64-.49h-.67v.99h.67Z" fill="currentColor"></path><path d="M54.72,24.84v10.93h-5.4V6.1h12.26c7.02,0,11.1,3.2,11.1,9.39s-4.07,9.35-11.06,9.35h-6.9,0ZM61.12,20.52c4.07,0,6.11-1.66,6.11-5.03s-2.04-5.03-6.11-5.03h-6.4v10.06h6.4Z" fill="currentColor"></path><path d="M85.94,32.45c-1,2.41-3.66,3.78-7.02,3.78-4.11,0-7.11-2.29-7.11-6.11,0-4.24,3.32-5.98,7.61-6.48l6.32-.71v-1c0-2.58-1.58-3.82-3.99-3.82s-3.74,1.29-3.91,3.24h-5.11c.46-4.53,3.99-7.19,9.18-7.19,5.74,0,9.02,2.7,9.02,8.19v8.15c0,1.95.08,3.58.42,5.28h-5.11c-.21-1.16-.29-2.29-.29-3.32h0ZM85.73,27.58v-1.29l-4.7.54c-2.24.29-3.95.79-3.95,2.99,0,1.66,1.16,2.7,3.28,2.7,2.74,0,5.36-1.62,5.36-4.95h0Z" fill="currentColor"></path><path d="M90.39,14.66h5.4l5.86,15.92h.08l5.57-15.92h5.28l-8.23,21.49c-2,5.28-4.45,7.32-8.89,7.36-.71,0-1.7-.08-2.45-.21v-4.03c.62.13.96.13,1.41.13,2.16,0,3.07-.75,4.2-3.66l-8.23-21.07h0Z" fill="currentColor"></path><path d="M113.46,35.77V6.1h5.32v29.67h-5.32Z" fill="currentColor"></path><path d="M130.79,36.27c-6.23,0-10.68-4.2-10.68-11.05s4.45-11.05,10.68-11.05,10.68,4.24,10.68,11.05-4.45,11.05-10.68,11.05ZM130.79,32.32c3.41,0,5.36-2.66,5.36-7.11s-1.95-7.11-5.36-7.11-5.36,2.7-5.36,7.11,1.91,7.11,5.36,7.11Z" fill="currentColor"></path><path d="M156.19,32.45c-1,2.41-3.66,3.78-7.02,3.78-4.11,0-7.11-2.29-7.11-6.11,0-4.24,3.32-5.98,7.61-6.48l6.32-.71v-1c0-2.58-1.58-3.82-3.99-3.82s-3.74,1.29-3.91,3.24h-5.11c.46-4.53,3.99-7.19,9.19-7.19,5.74,0,9.02,2.7,9.02,8.19v8.15c0,1.95.08,3.58.42,5.28h-5.11c-.21-1.16-.29-2.29-.29-3.32h0ZM155.98,27.58v-1.29l-4.7.54c-2.24.29-3.95.79-3.95,2.99,0,1.66,1.16,2.7,3.28,2.7,2.74,0,5.36-1.62,5.36-4.95h0Z" fill="currentColor"></path><path d="M178.5,32.41c-1.04,2.12-3.58,3.87-6.78,3.87-5.53,0-9.31-4.49-9.31-11.05s3.78-11.05,9.31-11.05c3.28,0,5.69,1.83,6.69,3.95V6.1h5.32v29.67h-5.24v-3.37h0ZM178.55,24.84c0-4.11-1.95-6.78-5.32-6.78s-5.45,2.83-5.45,7.15,2,7.15,5.45,7.15,5.32-2.66,5.32-6.78v-.75h0Z" fill="currentColor"></path></g></g></svg></a><div class="MobileNav_icons__4ORBL"><a class="MobileNav_github__cBYpY" href="https://github.com/payloadcms/payload" target="_blank" rel="noreferrer" aria-label="Payload's GitHub"><svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 96"><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z" fill="currentColor"></path></svg></a><div class="Docsearch_docSearch__tCi81"><!--$--><button type="button" class="DocSearch DocSearch-Button" aria-label="Search (Command+K)"><span class="DocSearch-Button-Container"><svg width="20" height="20" class="DocSearch-Search-Icon" viewBox="0 0 20 20" aria-hidden="true"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"></span></button><!--/$--></div><div class="MobileNav_modalToggler__CQSsz" aria-label="Open menu"><svg width="25" height="25" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="3.5" y="4.5" width="18" height="2" fill="currentColor"></rect><rect x="3.5" y="11.5" width="18" height="2" fill="currentColor"></rect><rect x="3.5" y="18.5" width="18" height="2" fill="currentColor"></rect></svg></div></div></div></div></div></div></div><!--$--><!--/$--></header></div><div><div class="RenderDocs_wrap__HtZBM Gutter_leftGutter__UWO6t Gutter_rightGutter__qUzUU"><div class="grid"><div aria-hidden="true"></div><main class="cols-8 start-5 cols-m-8 start-m-1 RenderDocs_content__XnnWb"><!--$--><h1 class="RenderDocs_title__h1NVD">Localization</h1><div class="RenderDocs_mdx__C8FvH"><p>Localization is one of the most important features of a modern CMS. It allows you to manage content in multiple languages, then serve it to your users based on their requested language. This is similar to <a href="./i18n">I18n</a>, but instead of managing translations for your application's interface, you are managing translations for the data itself.</p> <p>With Localization, you can begin to serve personalized content to your users based on their specific language preferences, such as a multilingual website or multi-site application. There are no limits to the number of locales you can add to your Payload project.</p> <p>To configure Localization, use the <span class="InlineCode_wrap__vw_Bo"><code>localization</code></span> key in your <a href="./overview">Payload Config</a>:</p> <div class="Code_codeWrap__cGPns Code_disableMinHeight__XTKwL Code_code__v2XQZ" data-theme="dark"><div class="Code_code__BTtCd code-block" style="color:#d1d5da"><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">1</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token keyword module" style="color:#ff7b72">import</span><span class="token plain"> </span><span class="token imports punctuation">{</span><span class="token imports"> buildConfig </span><span class="token imports punctuation">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#ff7b72">from</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'payload'</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">2</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain" style="display:inline-block"> </span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">3</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token keyword module" style="color:#ff7b72">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#ff7b72">default</span><span class="token plain"> </span><span class="token function" style="color:#d2a8ff">buildConfig</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">4</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// ...</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_ highlight-line" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">5</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">localization</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token punctuation">{</span><span class="token plain"> </span><span class="token comment" style="color:#8b949e"></span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">6</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// ...</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">7</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">8</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token punctuation">}</span><span class="token punctuation">)</span></div></div></div></div> <a class="Jumplist_node__Ir4tI" id="config-options" href="/docs/configuration/localization#config-options"><h2><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="Jumplist_linkedHeading__olF4F icons_icon__6EVpU icons_large__tRw9w"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>Config Options</h2></a> <p>Add the <span class="InlineCode_wrap__vw_Bo"><code>localization</code></span> property to your Payload Config to enable Localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options.</p> <p>To configure locales, use the <span class="InlineCode_wrap__vw_Bo"><code>localization.locales</code></span> property in your <a href="./overview">Payload Config</a>:</p> <div class="Code_codeWrap__cGPns Code_disableMinHeight__XTKwL Code_code__v2XQZ" data-theme="dark"><div class="Code_code__BTtCd code-block" style="color:#d1d5da"><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">1</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token keyword module" style="color:#ff7b72">import</span><span class="token plain"> </span><span class="token imports punctuation">{</span><span class="token imports"> buildConfig </span><span class="token imports punctuation">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#ff7b72">from</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'payload'</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">2</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain" style="display:inline-block"> </span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">3</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token keyword module" style="color:#ff7b72">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#ff7b72">default</span><span class="token plain"> </span><span class="token function" style="color:#d2a8ff">buildConfig</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">4</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// ...</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">5</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">localization</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">6</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">locales</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token string" style="color:#8cc4ff">'en'</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'es'</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'de'</span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// required</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">7</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">defaultLocale</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'en'</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// required</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">8</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">9</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token punctuation">}</span><span class="token punctuation">)</span></div></div></div></div> <p>You can also define locales using <a href="#locale-object">full configuration objects</a>:</p> <div class="Code_codeWrap__cGPns Code_disableMinHeight__XTKwL Code_code__v2XQZ" data-theme="dark"><div class="Code_code__BTtCd code-block" style="color:#d1d5da"><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">1</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token keyword module" style="color:#ff7b72">import</span><span class="token plain"> </span><span class="token imports punctuation">{</span><span class="token imports"> buildConfig </span><span class="token imports punctuation">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#ff7b72">from</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'payload'</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">2</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain" style="display:inline-block"> </span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">3</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token keyword module" style="color:#ff7b72">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#ff7b72">default</span><span class="token plain"> </span><span class="token function" style="color:#d2a8ff">buildConfig</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">4</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">collections</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">5</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// collections go here</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">6</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">7</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">localization</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">8</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">locales</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token punctuation">[</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">9</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">10</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">label</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'English'</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">11</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">code</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'en'</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">12</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">13</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">14</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">label</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'Arabic'</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">15</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">code</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'ar'</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">16</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// opt-in to setting default text-alignment on Input fields to rtl (right-to-left)</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">17</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// when current locale is rtl</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">18</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">rtl</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token boolean" style="color:#61afef">true</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">19</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">20</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">21</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">defaultLocale</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'en'</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// required</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">22</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">fallback</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token boolean" style="color:#61afef">true</span><span class="token punctuation">,</span><span class="token plain"> </span><span class="token comment" style="color:#8b949e">// defaults to true</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">23</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">24</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token punctuation">}</span><span class="token punctuation">)</span></div></div></div></div> <div class="Banner_banner___cbsO banner Banner_success__7k55f"><div class="Banner_children__fnU4H"><p><strong>Tip:</strong> Localization works very well alongside <a href="../configuration/i18n">I18n</a>.</p></div></div> <p>The following options are available:</p> <div class="Table_wrap__GSV9n"><table><thead><tr><th>Option</th><th>Description</th></tr></thead><tbody><tr><td><strong><span class="InlineCode_wrap__vw_Bo"><code>locales</code></span></strong></td><td>Array of all the languages that you would like to support. <a href="#locales">More details</a></td></tr><tr><td><strong><span class="InlineCode_wrap__vw_Bo"><code>defaultLocale</code></span></strong></td><td>Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale.</td></tr><tr><td><strong><span class="InlineCode_wrap__vw_Bo"><code>fallback</code></span></strong></td><td>Boolean enabling "fallback" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated unless a fallback is explicitly provided in the request. True by default.</td></tr></tbody></table></div> <a class="Jumplist_node__Ir4tI" id="locales" href="/docs/configuration/localization#locales"><h3><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="Jumplist_linkedHeading__olF4F icons_icon__6EVpU icons_large__tRw9w"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>Locales</h3></a> <p>The locales array is a list of all the languages that you would like to support. This can be strings for each language code, or <a href="#locale-object">full configuration objects</a> for more advanced options.</p> <p>The locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 3166‑1) such as <span class="InlineCode_wrap__vw_Bo"><code>en-US</code></span>, <span class="InlineCode_wrap__vw_Bo"><code>en-UK</code></span>, <span class="InlineCode_wrap__vw_Bo"><code>es-MX</code></span>, etc.</p> <a class="Jumplist_node__Ir4tI" id="locale-object" href="/docs/configuration/localization#locale-object"><h4><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="Jumplist_linkedHeading__olF4F icons_icon__6EVpU icons_large__tRw9w"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>Locale Object</h4></a> <div class="Table_wrap__GSV9n"><table><thead><tr><th>Option</th><th>Description</th></tr></thead><tbody><tr><td><strong><span class="InlineCode_wrap__vw_Bo"><code>code</code></span></strong> *</td><td>Unique code to identify the language throughout the APIs for <span class="InlineCode_wrap__vw_Bo"><code>locale</code></span> and <span class="InlineCode_wrap__vw_Bo"><code>fallbackLocale</code></span></td></tr><tr><td><strong><span class="InlineCode_wrap__vw_Bo"><code>label</code></span></strong></td><td>A string to use for the selector when choosing a language, or an object keyed on the i18n keys for different languages in use.</td></tr><tr><td><strong><span class="InlineCode_wrap__vw_Bo"><code>rtl</code></span></strong></td><td>A boolean that when true will make the admin UI display in Right-To-Left.</td></tr><tr><td><strong><span class="InlineCode_wrap__vw_Bo"><code>fallbackLocale</code></span></strong></td><td>The code for this language to fallback to when properties of a document are not present.</td></tr></tbody></table></div> <p><em>* An asterisk denotes that a property is required.</em></p> <a class="Jumplist_node__Ir4tI" id="field-localization" href="/docs/configuration/localization#field-localization"><h2><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="Jumplist_linkedHeading__olF4F icons_icon__6EVpU icons_large__tRw9w"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>Field Localization</h2></a> <p>Payload Localization works on a <strong>field</strong> level—not a document level. In addition to configuring the base Payload Config to support Localization, you need to specify each field that you would like to localize.</p> <p><strong>Here is an example of how to enable Localization for a field:</strong></p> <div class="Code_codeWrap__cGPns Code_disableMinHeight__XTKwL Code_code__v2XQZ" data-theme="dark"><div class="Code_code__BTtCd code-block" style="color:#d1d5da"><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">1</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">2</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">name</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'title'</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">3</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">type</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'text'</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_ highlight-line" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">4</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">localized</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token boolean" style="color:#61afef">true</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">5</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token punctuation">}</span></div></div></div></div> <p>With the above configuration, the <span class="InlineCode_wrap__vw_Bo"><code>title</code></span> field will now be saved in the database as an object of all locales instead of a single string.</p> <p>All field types with a <span class="InlineCode_wrap__vw_Bo"><code>name</code></span> property support the <span class="InlineCode_wrap__vw_Bo"><code>localized</code></span> property—even the more complex field types like <span class="InlineCode_wrap__vw_Bo"><code>array</code></span>s and <span class="InlineCode_wrap__vw_Bo"><code>block</code></span>s.</p> <div class="Banner_banner___cbsO banner Banner_info__uBu0x"><div class="Banner_children__fnU4H"><p><strong>Note:</strong> Enabling Localization for field types that support nested fields will automatically create localized "sets" of all fields contained within the field. For example, if you have a page layout using a blocks field type, you have the choice of either localizing the full layout, by enabling Localization on the top-level blocks field, or only certain fields within the layout.</p></div></div> <div class="Banner_banner___cbsO banner Banner_warning__t8CN8"><div class="Banner_children__fnU4H"><p><strong>Important:</strong> When converting an existing field to or from <span class="InlineCode_wrap__vw_Bo"><code>localized: true</code></span> the data structure in the document will change for this field and so existing data for this field will be lost. Before changing the Localization setting on fields with existing data, you may need to consider a field migration strategy.</p></div></div> <a class="Jumplist_node__Ir4tI" id="retrieving-localized-docs" href="/docs/configuration/localization#retrieving-localized-docs"><h2><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="Jumplist_linkedHeading__olF4F icons_icon__6EVpU icons_large__tRw9w"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>Retrieving Localized Docs</h2></a> <p>When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be used.</p> <a class="Jumplist_node__Ir4tI" id="rest-api" href="/docs/configuration/localization#rest-api"><h4><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="Jumplist_linkedHeading__olF4F icons_icon__6EVpU icons_large__tRw9w"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>REST API</h4></a> <p>REST API locale functionality relies on URL query parameters.</p> <p><strong><span class="InlineCode_wrap__vw_Bo"><code>?locale=</code></span></strong></p> <p>Specify your desired locale by providing the <span class="InlineCode_wrap__vw_Bo"><code>locale</code></span> query parameter directly in the endpoint URL.</p> <p><strong><span class="InlineCode_wrap__vw_Bo"><code>?fallback-locale=</code></span></strong></p> <p>Specify fallback locale to be used by providing the <span class="InlineCode_wrap__vw_Bo"><code>fallback-locale</code></span> query parameter. This can be provided as either a valid locale as provided to your base Payload Config, or <span class="InlineCode_wrap__vw_Bo"><code>'null'</code></span>, <span class="InlineCode_wrap__vw_Bo"><code>'false'</code></span>, or <span class="InlineCode_wrap__vw_Bo"><code>'none'</code></span> to disable falling back.</p> <p><strong>Example:</strong></p> <div class="Code_codeWrap__cGPns Code_disableMinHeight__XTKwL Code_code__v2XQZ" data-theme="dark"><div class="Code_code__BTtCd code-block" style="color:#d1d5da"><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">1</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token function" style="color:#d2a8ff">fetch</span><span class="token punctuation">(</span><span class="token string" style="color:#8cc4ff">'https://localhost:3000/api/pages?locale=es&fallback-locale=none'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></div></div></div></div> <a class="Jumplist_node__Ir4tI" id="graphql-api" href="/docs/configuration/localization#graphql-api"><h4><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="Jumplist_linkedHeading__olF4F icons_icon__6EVpU icons_large__tRw9w"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>GraphQL API</h4></a> <p>In the GraphQL API, you can specify <span class="InlineCode_wrap__vw_Bo"><code>locale</code></span> and <span class="InlineCode_wrap__vw_Bo"><code>fallbackLocale</code></span> args to all relevant queries and mutations.</p> <p>The <span class="InlineCode_wrap__vw_Bo"><code>locale</code></span> arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum values (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious to see how locales are auto-formatted, you can use the <a href="../graphql/overview#graphql-playground">GraphQL playground</a>.</p> <p>The <span class="InlineCode_wrap__vw_Bo"><code>fallbackLocale</code></span> arg will accept valid locales as well as <span class="InlineCode_wrap__vw_Bo"><code>none</code></span> to disable falling back.</p> <p><strong>Example:</strong></p> <div class="Code_codeWrap__cGPns Code_disableMinHeight__XTKwL Code_code__v2XQZ" data-theme="dark"><div class="Code_code__BTtCd code-block" style="color:#d1d5da"><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">1</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain">query </span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">2</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token function maybe-class-name" style="color:#e5c07b">Posts</span><span class="token punctuation">(</span><span class="token parameter literal-property property" style="color:#61afef">locale</span><span class="token parameter operator" style="color:#61afef">:</span><span class="token parameter"> de</span><span class="token parameter punctuation">,</span><span class="token parameter"> </span><span class="token parameter literal-property property" style="color:#61afef">fallbackLocale</span><span class="token parameter operator" style="color:#61afef">:</span><span class="token parameter"> none</span><span class="token punctuation">)</span><span class="token plain"> </span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">3</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> docs </span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">4</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> title</span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">5</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">}</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">6</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token punctuation">}</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">7</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token punctuation">}</span></div></div></div></div> <div class="Banner_banner___cbsO banner Banner_default__NT5rD"><div class="Banner_children__fnU4H"><p>In GraphQL, specifying the locale at the top level of a query will automatically apply it throughout all nested relationship fields. You can override this behavior by re-specifying locale arguments in nested related document queries.</p></div></div> <a class="Jumplist_node__Ir4tI" id="local-api" href="/docs/configuration/localization#local-api"><h4><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="Jumplist_linkedHeading__olF4F icons_icon__6EVpU icons_large__tRw9w"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>Local API</h4></a> <p>You can specify <span class="InlineCode_wrap__vw_Bo"><code>locale</code></span> as well as <span class="InlineCode_wrap__vw_Bo"><code>fallbackLocale</code></span> within the Local API as well as properties on the <span class="InlineCode_wrap__vw_Bo"><code>options</code></span> argument. The <span class="InlineCode_wrap__vw_Bo"><code>locale</code></span> property will accept any valid locale, and the <span class="InlineCode_wrap__vw_Bo"><code>fallbackLocale</code></span> property will accept any valid locale as well as <span class="InlineCode_wrap__vw_Bo"><code>'null'</code></span>, <span class="InlineCode_wrap__vw_Bo"><code>'false'</code></span>, <span class="InlineCode_wrap__vw_Bo"><code>false</code></span>, and <span class="InlineCode_wrap__vw_Bo"><code>'none'</code></span>.</p> <p><strong>Example:</strong></p> <div class="Code_codeWrap__cGPns Code_disableMinHeight__XTKwL Code_code__v2XQZ" data-theme="dark"><div class="Code_code__BTtCd code-block" style="color:#d1d5da"><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">1</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token keyword" style="color:#ff7b72">const</span><span class="token plain"> posts </span><span class="token operator" style="color:#61afef">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#ff7b72">await</span><span class="token plain"> payload</span><span class="token punctuation">.</span><span class="token method function property-access" style="color:#61afef">find</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">2</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">collection</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'posts'</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">3</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">locale</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token string" style="color:#8cc4ff">'es'</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">4</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"> </span><span class="token literal-property property" style="color:#61afef">fallbackLocale</span><span class="token operator" style="color:#61afef">:</span><span class="token plain"> </span><span class="token boolean" style="color:#61afef">false</span><span class="token punctuation">,</span><span class="token plain"></span></div></div><div class="token-line Code_line__q5GJ_" style="color:#d1d5da"><span class="Code_lineNumber__XZgDa">5</span><div class="Code_lineCodeWrapper__g0OaV"><span class="token plain"></span><span class="token punctuation">}</span><span class="token punctuation">)</span></div></div></div></div> <div class="Banner_banner___cbsO banner Banner_success__7k55f"><div class="Banner_children__fnU4H"><p><strong>Tip:</strong> The REST and Local APIs can return all Localization data in one request by passing 'all' or '*' as the <strong>locale</strong> parameter. The response will be structured so that field values come back as the full objects keyed for each locale instead of the single, translated value.</p></div></div></div><!--/$--><a class="RenderDocs_next__wMNfO RenderDocs_hasRelatedThreads__kdwC3" data-algolia-no-crawl="true" href="/docs/configuration/environment-vars"><div class="RenderDocs_nextLabel__xEloF">Next <svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></div><h3>Environment Variables</h3><div aria-hidden="true" class="BackgroundScanline_wrapper__TTtqa"><div class="BackgroundScanline_backgroundScanline__iS5AQ"></div><svg xmlns="http://www.w3.org/2000/svg" class="BackgroundScanline_crosshair__Au3az BackgroundScanline_crosshairTopLeft__MzINp crosshair icons_icon__6EVpU icons_large__tRw9w" width="20" height="21" viewBox="0 0 20 21" fill="none" stroke="currentColor"><path d="M10 0.332031V20.332"></path><path d="M0 10.332L20 10.332"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="BackgroundScanline_crosshair__Au3az BackgroundScanline_crosshairBottomLeft__vRreZ crosshair icons_icon__6EVpU icons_large__tRw9w" width="20" height="21" viewBox="0 0 20 21" fill="none" stroke="currentColor"><path d="M10 0.332031V20.332"></path><path d="M0 10.332L20 10.332"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="BackgroundScanline_crosshair__Au3az BackgroundScanline_crosshairTopRight__c1rwU crosshair icons_icon__6EVpU icons_large__tRw9w" width="20" height="21" viewBox="0 0 20 21" fill="none" stroke="currentColor"><path d="M10 0.332031V20.332"></path><path d="M0 10.332L20 10.332"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="BackgroundScanline_crosshair__Au3az BackgroundScanline_crosshairBottomRight__ISe0a crosshair icons_icon__6EVpU icons_large__tRw9w" width="20" height="21" viewBox="0 0 20 21" fill="none" stroke="currentColor"><path d="M10 0.332031V20.332"></path><path d="M0 10.332L20 10.332"></path></svg></div></a><div class="RelatedHelpList_relatedHelpList__5N3gz"><div class="RelatedHelpList_titleWrapper__2VjfE"><h4 class="RelatedHelpList_title__IkeKR">Related Help Topics</h4></div><ul><li><svg class="RelatedHelpList_itemMarker__uRSIP" width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.2129 18.8555C11.5352 19.1025 11.9258 19.248 12.3457 19.248C13.4512 19.248 14.3066 18.2744 14.3242 17.0752C14.3438 15.877 13.4551 14.8936 12.3418 14.8936C11.2285 14.8936 10.3633 15.877 10.3633 17.0752C10.3633 17.8115 10.7012 18.4629 11.2129 18.8555Z" fill="currentColor"></path><path d="M17.6758 17.0752C17.6758 18.2744 18.5684 19.248 19.6543 19.248C20.7598 19.248 21.6133 18.2744 21.6328 17.0752C21.6523 15.877 20.7695 14.8936 19.6543 14.8936C18.541 14.8936 17.6758 15.877 17.6758 17.0752Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM19.0977 8C20.6641 8.2627 22.1914 8.72656 23.6367 9.38184C26.127 12.9844 27.3633 17.0479 26.9062 21.7363C25.2246 22.9678 23.3398 23.9023 21.3359 24.5C20.8848 23.9014 20.4863 23.2656 20.1445 22.5996C20.7969 22.3584 21.4258 22.0605 22.0254 21.7109C21.8691 21.6064 21.7148 21.4893 21.5645 21.3682C19.8242 22.1787 17.9238 22.5986 16 22.5986C14.0762 22.5986 12.1758 22.1787 10.4355 21.3682C10.2871 21.4814 10.1328 21.5977 9.97461 21.7109C10.2852 21.8926 10.6035 22.0596 10.9297 22.2119C11.2305 22.3535 11.5391 22.4824 11.8516 22.5977C11.5098 23.2637 11.1113 23.9004 10.6602 24.5C9.34961 24.1064 8.08984 23.5703 6.9043 22.9014C6.2793 22.5488 5.67383 22.1592 5.09375 21.7344C4.70312 17.6914 5.48242 13.5908 8.35547 9.38574C9.80273 8.73047 11.3281 8.26465 12.8965 8C13.1113 8.37988 13.3066 8.76953 13.4785 9.16992C14.3887 9.03418 15.3086 8.97266 16.2246 8.98535C16.9902 8.99512 17.7559 9.05664 18.5156 9.16992C18.6133 8.94434 18.7168 8.72266 18.8281 8.50391C18.9141 8.33398 19.0039 8.16602 19.0977 8Z" fill="currentColor"></path></svg><a href="/community-help/discord/localize-errors">Localize Errors</a></li><li><svg class="RelatedHelpList_itemMarker__uRSIP" width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.2129 18.8555C11.5352 19.1025 11.9258 19.248 12.3457 19.248C13.4512 19.248 14.3066 18.2744 14.3242 17.0752C14.3438 15.877 13.4551 14.8936 12.3418 14.8936C11.2285 14.8936 10.3633 15.877 10.3633 17.0752C10.3633 17.8115 10.7012 18.4629 11.2129 18.8555Z" fill="currentColor"></path><path d="M17.6758 17.0752C17.6758 18.2744 18.5684 19.248 19.6543 19.248C20.7598 19.248 21.6133 18.2744 21.6328 17.0752C21.6523 15.877 20.7695 14.8936 19.6543 14.8936C18.541 14.8936 17.6758 15.877 17.6758 17.0752Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM19.0977 8C20.6641 8.2627 22.1914 8.72656 23.6367 9.38184C26.127 12.9844 27.3633 17.0479 26.9062 21.7363C25.2246 22.9678 23.3398 23.9023 21.3359 24.5C20.8848 23.9014 20.4863 23.2656 20.1445 22.5996C20.7969 22.3584 21.4258 22.0605 22.0254 21.7109C21.8691 21.6064 21.7148 21.4893 21.5645 21.3682C19.8242 22.1787 17.9238 22.5986 16 22.5986C14.0762 22.5986 12.1758 22.1787 10.4355 21.3682C10.2871 21.4814 10.1328 21.5977 9.97461 21.7109C10.2852 21.8926 10.6035 22.0596 10.9297 22.2119C11.2305 22.3535 11.5391 22.4824 11.8516 22.5977C11.5098 23.2637 11.1113 23.9004 10.6602 24.5C9.34961 24.1064 8.08984 23.5703 6.9043 22.9014C6.2793 22.5488 5.67383 22.1592 5.09375 21.7344C4.70312 17.6914 5.48242 13.5908 8.35547 9.38574C9.80273 8.73047 11.3281 8.26465 12.8965 8C13.1113 8.37988 13.3066 8.76953 13.4785 9.16992C14.3887 9.03418 15.3086 8.97266 16.2246 8.98535C16.9902 8.99512 17.7559 9.05664 18.5156 9.16992C18.6133 8.94434 18.7168 8.72266 18.8281 8.50391C18.9141 8.33398 19.0039 8.16602 19.0977 8Z" fill="currentColor"></path></svg><a href="/community-help/discord/query-specific-collection-with-all-locales">Query specific collection with all locales</a></li><li><svg class="RelatedHelpList_itemMarker__uRSIP" width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.2129 18.8555C11.5352 19.1025 11.9258 19.248 12.3457 19.248C13.4512 19.248 14.3066 18.2744 14.3242 17.0752C14.3438 15.877 13.4551 14.8936 12.3418 14.8936C11.2285 14.8936 10.3633 15.877 10.3633 17.0752C10.3633 17.8115 10.7012 18.4629 11.2129 18.8555Z" fill="currentColor"></path><path d="M17.6758 17.0752C17.6758 18.2744 18.5684 19.248 19.6543 19.248C20.7598 19.248 21.6133 18.2744 21.6328 17.0752C21.6523 15.877 20.7695 14.8936 19.6543 14.8936C18.541 14.8936 17.6758 15.877 17.6758 17.0752Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM19.0977 8C20.6641 8.2627 22.1914 8.72656 23.6367 9.38184C26.127 12.9844 27.3633 17.0479 26.9062 21.7363C25.2246 22.9678 23.3398 23.9023 21.3359 24.5C20.8848 23.9014 20.4863 23.2656 20.1445 22.5996C20.7969 22.3584 21.4258 22.0605 22.0254 21.7109C21.8691 21.6064 21.7148 21.4893 21.5645 21.3682C19.8242 22.1787 17.9238 22.5986 16 22.5986C14.0762 22.5986 12.1758 22.1787 10.4355 21.3682C10.2871 21.4814 10.1328 21.5977 9.97461 21.7109C10.2852 21.8926 10.6035 22.0596 10.9297 22.2119C11.2305 22.3535 11.5391 22.4824 11.8516 22.5977C11.5098 23.2637 11.1113 23.9004 10.6602 24.5C9.34961 24.1064 8.08984 23.5703 6.9043 22.9014C6.2793 22.5488 5.67383 22.1592 5.09375 21.7344C4.70312 17.6914 5.48242 13.5908 8.35547 9.38574C9.80273 8.73047 11.3281 8.26465 12.8965 8C13.1113 8.37988 13.3066 8.76953 13.4785 9.16992C14.3887 9.03418 15.3086 8.97266 16.2246 8.98535C16.9902 8.99512 17.7559 9.05664 18.5156 9.16992C18.6133 8.94434 18.7168 8.72266 18.8281 8.50391C18.9141 8.33398 19.0039 8.16602 19.0977 8Z" fill="currentColor"></path></svg><a href="/community-help/discord/would-moving-localisation-to-its-own-collections-similar-to-versions-reduce-fear-of-migration">Would moving localisation to its own collections similar to versions reduce fear of migration?</a></li></ul></div></main><aside class="cols-3 start-14 RenderDocs_aside__2YzK7"><div class="RenderDocs_asideStickyContent__6tCbZ"><div class="VersionSelector_wrapper__7PoCH"><select aria-label="Select Version" class="VersionSelector_select__W7wMu"><option class=" " label="Version 3" value="latest"></option><option class=" " label="Version 2" value="v2"></option></select><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="VersionSelector_icon__O8gJD"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"></path></svg></div><div class="TableOfContents_wrap__CXNv1"><h6 class="TableOfContents_tocTitle__KSCJ3">On this page</h6><ul class="TableOfContents_toc__ENyvI"><li><a href="/docs/configuration/localization#config-options"><div class="TableOfContents_heading-2__elAr3 TableOfContents_heading__i3RHA">Config Options</div></a></li><li><a href="/docs/configuration/localization#locales"><div class="TableOfContents_heading-3__qmxuS TableOfContents_heading__i3RHA">Locales</div></a></li><li><a href="/docs/configuration/localization#field-localization"><div class="TableOfContents_heading-2__elAr3 TableOfContents_heading__i3RHA">Field Localization</div></a></li><li><a href="/docs/configuration/localization#retrieving-localized-docs"><div class="TableOfContents_heading-2__elAr3 TableOfContents_heading__i3RHA">Retrieving Localized Docs</div></a></li></ul></div><div class="RenderDocs_discordGitWrap__MRSs_"><div class="DiscordGitCTA_ctaWrap__tS4HQ"><a class="DiscordGitCTA_cta__TpUYy" target="_blank" href="https://github.com/payloadcms/payload"><div class="DiscordGitCTA_message__Lq_Nt">Star on GitHub<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DiscordGitCTA_arrow__4uqoA icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></div><div class="DiscordGitCTA_gitButton__QBm7t"><div class="GithubStarsPill_pill__ebw9m"><div class="GithubStarsPill_leftPill__WJti_"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path fill="currentColor" d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path></svg><p>Star</p></div><div class="GithubStarsPill_starCount__0x9VE"></div></div></div></a><a aria-label="Chat on Discord" class="DiscordGitCTA_cta__TpUYy" target="_blank" href="https://discord.gg/FSn5QRdsbC"><div class="DiscordGitCTA_message__Lq_Nt">Chat on Discord<svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="DiscordGitCTA_arrow__4uqoA icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></div><div class="DiscordGitCTA_discordButton__k9tbH"><div class="DiscordUsersPill_pill__hy0w3"><p class="DiscordUsersPill_leftPill__NueM8">Discord</p><p class="DiscordUsersPill_userCount__lciyZ"> online</p></div></div></a></div></div><div class="Feedback_feedbackWrapper__tRn5l"><a class="Feedback_gitHubLink__XEOgg" target="_blank" href="https://github.com/payloadcms/payload/blob/main/docs/configuration/localization.mdx">Edit this page on GitHub <svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="Feedback_arrow__oXpS9 icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></a><button type="button" class="Feedback_drawerButton__h47Sa">Leave feedback <svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="Feedback_arrow__oXpS9 icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg></button></div></div></aside></div><div aria-hidden="true" class="BackgroundGrid_backgroundGrid__oXHXt grid BackgroundGrid_wideGrid__CNoDS" style="z-index:-1"><div class="BackgroundGrid_column__Q_qjH cols-4"></div><div class="BackgroundGrid_column__Q_qjH cols-4"></div><div class="BackgroundGrid_column__Q_qjH cols-4"></div><div class="BackgroundGrid_column__Q_qjH cols-4"></div></div></div><div id="docsearch"></div><footer class="Footer_footer__9S97q" data-theme="dark"><div aria-hidden="true" class="BackgroundGrid_backgroundGrid__oXHXt grid" style="z-index:2"><div class="BackgroundGrid_column__Q_qjH cols-4"></div><div class="BackgroundGrid_column__Q_qjH cols-4"></div><div class="BackgroundGrid_column__Q_qjH cols-4"></div><div class="BackgroundGrid_column__Q_qjH cols-4"></div><div class="BackgroundGrid_column__Q_qjH cols-4"></div></div><div class="Footer_container__kRlyj Gutter_leftGutter__UWO6t Gutter_rightGutter__qUzUU"><div class="Footer_grid__dCvBf grid"><div class="cols-4 cols-m-8 cols-s-8"><p class="Footer_colHeader__iIdhD">Use Cases</p><div class="Footer_colItems__U716j"><a class="Footer_link__HfGPU" id="" href="/use-cases/headless-cms">Content Management System</a><a class="Footer_link__HfGPU" id="" href="/use-cases/enterprise-app-builder">Enterprise App Builder</a><a class="Footer_link__HfGPU" id="" href="/use-cases/headless-ecommerce">Headless E-Commerce</a><a class="Footer_link__HfGPU" id="" href="/use-cases/digital-asset-management">Digital Asset Management</a></div></div><div class="cols-4 cols-m-8 cols-s-8"><p class="Footer_colHeader__iIdhD">Developers</p><div class="Footer_colItems__U716j"><a class="Footer_link__HfGPU" id="" href="/new">Payload Cloud</a><a class="Footer_link__HfGPU" id="" href="/docs/getting-started/what-is-payload">Documentation</a><a class="Footer_link__HfGPU" id="" href="/community-help">Community Help</a><a href="https://github.com/payloadcms/payload/discussions/categories/roadmap?discussions_q=category%3ARoadmap+" target="_blank" rel="noopener noreferrer" class="Footer_link__HfGPU" id="">Roadmap</a><a href="https://github.com/payloadcms/payload/tree/main/templates" target="_blank" rel="noopener noreferrer" class="Footer_link__HfGPU" id="">Templates</a><a href="https://github.com/payloadcms/payload/releases" target="_blank" rel="noopener noreferrer" class="Footer_link__HfGPU" id="">Releases</a></div></div><div class="cols-4 cols-m-8 cols-s-8"><p class="Footer_colHeader__iIdhD">Company</p><div class="Footer_colItems__U716j"><a class="Footer_link__HfGPU" id="" href="/cloud-pricing">Pricing</a><a class="Footer_link__HfGPU" id="" href="/talk-to-us">Enterprise</a><a class="Footer_link__HfGPU" id="" href="/case-studies">Case Studies</a><a class="Footer_link__HfGPU" id="" href="/become-a-partner">Partner With Us</a><a class="Footer_link__HfGPU" id="" href="/partners">Find a Partner</a><a class="Footer_link__HfGPU" id="" href="/blog">Blog</a><a class="Footer_link__HfGPU" id="" href="/security">Security</a><a class="Footer_link__HfGPU" id="" href="/compare">Compare Payload</a><a class="Footer_link__HfGPU" id="" href="/terms">Terms of Service</a><a class="Footer_link__HfGPU" id="" href="/privacy">Privacy Policy</a><a class="Footer_link__HfGPU" id="" href="/contact">Contact</a></div></div><div class="cols-4 cols-m-4 cols-s-8"><p class="Footer_colHeader__iIdhD undefined">Stay connected</p><div class="NewsletterSignUp_newsletterSignUp__ZJvAv"><form noValidate=""><div class="NewsletterSignUp_inputWrap__DHMO7"><label class="visually-hidden" for=":Rabqtd6lfb:">Subscribe to our newsletter</label><div class="NewsletterSignUp_emailInput__dT0z2 Text_component__cVYzY Text_fullWidth__4xkvV"><div class="Text_inputWrap__iPtkX"><input autoComplete="off" autoCorrect="off" autoCapitalize="none" class="Text_input__SACMN" placeholder="Enter your email" type="text" id=":Rabqtd6lfb:" name="email" value=""/></div></div><button class="NewsletterSignUp_submitButton__hBvFi" disabled="" type="submit"><svg width="100%" height="100%" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg" class="NewsletterSignUp_inputArrow__Tbhpn icons_icon__6EVpU"><path d="M1 12L12.5 0.499965" class="icons_stroke__FrZ8d"></path><path d="M1 0.5H12.5V12" class="icons_stroke__FrZ8d"></path></svg><span class="visually-hidden">Submit</span></button></div><div class="NewsletterSignUp_subscribeAction__VLO0i"><p class="NewsletterSignUp_subscribeDesc__KVjjy"></p></div></form></div><div class="Footer_socialLinks__C9U62"><a href="https://twitter.com/payloadcms" target="_blank" rel="noopener noreferrer" class="Footer_socialIconLink__YYgZK undefined" aria-label="Payload's Twitter page"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M21.8496 22.7227H20.2051L9.4668 8.68359H11.2324L21.8496 22.7227Z" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM24.2168 7H21.248L16.3555 12.5918L12.125 7H6L13.3203 16.5713L6.38281 24.5H9.35352L14.707 18.3818L19.3867 24.5H25.3594L17.7305 14.4131L24.2168 7Z" fill="white"></path></svg></a><a href="https://discord.com/invite/r6sCXqVk3v" target="_blank" rel="noopener noreferrer" class="Footer_socialIconLink__YYgZK" aria-label="Payload's Discord"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.2129 18.8555C11.5352 19.1025 11.9258 19.248 12.3457 19.248C13.4512 19.248 14.3066 18.2744 14.3242 17.0752C14.3438 15.877 13.4551 14.8936 12.3418 14.8936C11.2285 14.8936 10.3633 15.877 10.3633 17.0752C10.3633 17.8115 10.7012 18.4629 11.2129 18.8555Z" fill="currentColor"></path><path d="M17.6758 17.0752C17.6758 18.2744 18.5684 19.248 19.6543 19.248C20.7598 19.248 21.6133 18.2744 21.6328 17.0752C21.6523 15.877 20.7695 14.8936 19.6543 14.8936C18.541 14.8936 17.6758 15.877 17.6758 17.0752Z" fill="currentColor"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM19.0977 8C20.6641 8.2627 22.1914 8.72656 23.6367 9.38184C26.127 12.9844 27.3633 17.0479 26.9062 21.7363C25.2246 22.9678 23.3398 23.9023 21.3359 24.5C20.8848 23.9014 20.4863 23.2656 20.1445 22.5996C20.7969 22.3584 21.4258 22.0605 22.0254 21.7109C21.8691 21.6064 21.7148 21.4893 21.5645 21.3682C19.8242 22.1787 17.9238 22.5986 16 22.5986C14.0762 22.5986 12.1758 22.1787 10.4355 21.3682C10.2871 21.4814 10.1328 21.5977 9.97461 21.7109C10.2852 21.8926 10.6035 22.0596 10.9297 22.2119C11.2305 22.3535 11.5391 22.4824 11.8516 22.5977C11.5098 23.2637 11.1113 23.9004 10.6602 24.5C9.34961 24.1064 8.08984 23.5703 6.9043 22.9014C6.2793 22.5488 5.67383 22.1592 5.09375 21.7344C4.70312 17.6914 5.48242 13.5908 8.35547 9.38574C9.80273 8.73047 11.3281 8.26465 12.8965 8C13.1113 8.37988 13.3066 8.76953 13.4785 9.16992C14.3887 9.03418 15.3086 8.97266 16.2246 8.98535C16.9902 8.99512 17.7559 9.05664 18.5156 9.16992C18.6133 8.94434 18.7168 8.72266 18.8281 8.50391C18.9141 8.33398 19.0039 8.16602 19.0977 8Z" fill="currentColor"></path></svg></a><a href="https://www.youtube.com/channel/UCyrx4Wpd4SBIpqUKlkb6N1Q" target="_blank" rel="noopener noreferrer" class="Footer_socialIconLink__YYgZK" aria-label="Payload's YouTube channel"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.625 19.1064V12.8848L19.959 15.9902L13.625 19.1064Z" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM22.0293 9.14355C19.1758 8.95215 12.8203 8.95312 9.9707 9.14355C6.88672 9.34961 6.52344 11.1807 6.5 16C6.52344 20.8105 6.88281 22.6494 9.9707 22.8564C12.8223 23.0469 19.1758 23.0479 22.0293 22.8564C25.1133 22.6504 25.4766 20.8193 25.5 16C25.4766 11.1895 25.1172 9.35059 22.0293 9.14355Z" fill="white"></path></svg></a><a href="https://www.instagram.com/payloadcms/" target="_blank" rel="noopener noreferrer" class="Footer_socialIconLink__YYgZK" aria-label="Payload's Instagram page"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12.834 16.5049C12.834 18.2539 14.252 19.6709 16 19.6709C17.75 19.6709 19.166 18.2539 19.166 16.5049C19.166 14.7559 17.75 13.3379 16 13.3379C14.252 13.3379 12.834 14.7559 12.834 16.5049Z" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M17.7168 8.71191L16 8.70898C13.4629 8.70898 13.1621 8.71875 12.1602 8.76367C9.58398 8.88086 8.38477 10.1035 8.26562 12.6582C8.2207 13.6602 8.21094 13.96 8.21094 16.4971C8.21094 19.0342 8.2207 19.334 8.26562 20.335C8.38281 22.8857 9.58008 24.1123 12.1602 24.2305C13.1621 24.2764 13.4629 24.2861 16 24.2861C18.5371 24.2861 18.8359 24.2764 19.8379 24.2305C22.416 24.1133 23.6152 22.8896 23.7324 20.3359C23.7773 19.335 23.7871 19.0352 23.7871 16.4971C23.7871 13.9609 23.7793 13.6602 23.7324 12.6582C23.6152 10.1035 22.4141 8.88184 19.8379 8.76465C19.127 8.73242 18.7695 8.71777 17.7168 8.71191ZM15.998 11.6289C13.3027 11.6289 11.1191 13.8135 11.1191 16.5068C11.1191 19.2012 13.3027 21.3857 15.998 21.3857C18.6914 21.3857 20.875 19.2021 20.875 16.5068C20.875 13.8135 18.6914 11.6289 15.998 11.6289ZM21.0723 10.293C20.4414 10.293 19.9297 10.8037 19.9297 11.4326C19.9297 11.7832 20.0879 12.0967 20.3379 12.3057C20.5371 12.4727 20.793 12.5732 21.0723 12.5732C21.7012 12.5732 22.2109 12.0625 22.2109 11.4326C22.2109 10.8037 21.7012 10.293 21.0723 10.293Z" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM13.7266 7.00781C14.2324 7.00195 14.918 7 16 7C18.5801 7 18.9043 7.01074 19.918 7.05762C23.3711 7.21582 25.2871 9.13672 25.4434 12.584C25.4883 13.5967 25.5 13.9199 25.5 16.5C25.5 19.0801 25.4883 19.4043 25.4414 20.417C25.2871 23.8701 23.3633 25.7842 19.918 25.9434C18.9043 25.9893 18.5801 26 16 26C13.4199 26 13.0977 25.9893 12.082 25.9434C8.62891 25.7842 6.71484 23.8672 6.55664 20.417C6.51172 19.4043 6.5 19.0801 6.5 16.5C6.5 13.9199 6.51172 13.5967 6.55859 12.583C6.7168 9.12988 8.63281 7.21582 12.084 7.05664C12.6719 7.03027 13.0273 7.01562 13.7266 7.00781Z" fill="white"></path></svg></a></div><div class="Footer_selectContainer__mD7sO"><label class="visually-hidden" for=":R1qtd6lfb:">Switch themes</label><select id=":R1qtd6lfb:"><option value="auto">Auto</option><option value="light">Light</option><option value="dark">Dark</option></select><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="Footer_switcherIcon__E7HAb Footer_upDownChevronIcon__VreT9"><path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9"></path></svg></div></div></div></div><div class="Gutter_leftGutter__UWO6t Gutter_rightGutter__qUzUU"><div class="Payload3D_container__j0ydN" data-theme="dark"><div class="Payload3D_mask__w6NaG"><div class="Payload3D_noise__pniGe"></div><div class="Payload3D_gradient__JetIl" style="--mouse-x:0;--mouse-y:0"></div></div></div></div></footer></div><div class="modal-container"></div></div><div style="width:var(--gutter-h)"></div><script src="/_next/static/chunks/webpack-7fa974e68fcbf6b9.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1b:\"$Sreact.fragment\"\n1c:I[23189,[],\"\"]\n1d:I[85457,[],\"\"]\n21:I[75098,[],\"OutletBoundary\"]\n23:I[75098,[],\"MetadataBoundary\"]\n25:I[75098,[],\"ViewportBoundary\"]\n27:I[86490,[\"4219\",\"static/chunks/app/global-error-f07b6b99d52dc53c.js\"],\"default\"]\n1:HL[\"/_next/static/media/096fc0a920206bee-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n2:HL[\"/_next/static/media/0a519dad0c4c2790-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n3:HL[\"/_next/static/media/32d694b729e49e86-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n4:HL[\"/_next/static/media/5566c67f1508ae8a-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n5:HL[\"/_next/static/media/66f30814ff6d7cdf.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n6:HL[\"/_next/static/media/716743f5eba8aa5c-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n7:HL[\"/_next/static/media/77e7188387b00f5d-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n8:HL[\"/_next/static/media/bb521019c7c7526d-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n9:HL[\"/_next/static/media/c86d5e148479770d-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\na:HL[\"/_next/static/media/d8aa0c7d96365198-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\nb:HL[\"/_next/static/media/fbc69893487b1118-s.p.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\nc:HL[\"/_next/static/css/390d0663ad838fdc.css\",\"style\"]\nd:HL[\"/_next/static/css/26afb2933518d3bd.css\",\"style\"]\ne:HL[\"/_next/static/css/e7ca63aea2683535.css\",\"style\"]\nf:HL[\"/_next/static/css/fdb2722c82ebf619.css\",\"style\"]\n10:HL[\"/_next/static/css/14c8222b4e6bfb9e.css\",\"style\"]\n11:HL[\"/_next/static/css/f574d745d6edfb67.css\",\"style\"]\n12:HL[\"/_next/static/css/b1cf855db6ac51b1.css\",\"style\"]\n13:HL[\"/_next/static/css/ec76de1699f3bb1c.css\",\"style\"]\n14:HL[\"/_next/static/css/ca44753475331c48.css\",\"style\"]\n15:HL[\"/_next/static/css/e4938badb52137ae.css\",\"style\"]\n16:HL[\"/_next/static/css/3335bd7359ecbb8a.css\",\"style\"]\n17:HL[\"/_next/static/css/26e2d81bd32c127c.css\",\"style\"]\n18:HL[\"/_next/static/css/fe14"])</script><script>self.__next_f.push([1,"b9b58f5d10c1.css\",\"style\"]\n19:HL[\"/_next/static/css/972e81b151cbc342.css\",\"style\"]\n1a:HL[\"/_next/static/css/add6d6451a368571.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"b\":\"cGuiW3oZ7uzQoNpflEu2G\",\"p\":\"\",\"c\":[\"\",\"docs\",\"configuration\",\"localization\"],\"i\":false,\"f\":[[[\"\",{\"children\":[\"(frontend)\",{\"children\":[\"(pages)\",{\"children\":[\"docs\",{\"children\":[[\"topic\",\"configuration\",\"d\"],{\"children\":[[\"doc\",\"localization\",\"d\"],{\"children\":[\"__PAGE__\",{}]}]}]}]}]},\"$undefined\",\"$undefined\",true]}],[\"\",[\"$\",\"$1b\",\"c\",{\"children\":[null,[\"$\",\"$L1c\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L1d\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[\"$\",\"title\",null,{\"children\":\"404: This page could not be found.\"}],[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"textAlign\":\"center\",\"display\":\"flex\",\"flexDirection\":\"column\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"children\":[[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\"}}],[\"$\",\"h1\",null,{\"className\":\"next-error-h1\",\"style\":{\"display\":\"inline-block\",\"margin\":\"0 20px 0 0\",\"padding\":\"0 23px 0 0\",\"fontSize\":24,\"fontWeight\":500,\"verticalAlign\":\"top\",\"lineHeight\":\"49px\"},\"children\":\"404\"}],[\"$\",\"div\",null,{\"style\":{\"display\":\"inline-block\"},\"children\":[\"$\",\"h2\",null,{\"style\":{\"fontSize\":14,\"fontWeight\":400,\"lineHeight\":\"49px\",\"margin\":0},\"children\":\"This page could not be found.\"}]}]]}]}]],\"notFoundStyles\":[]}]]}],{\"children\":[\"(frontend)\",[\"$\",\"$1b\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/390d0663ad838fdc.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/26afb2933518d3bd.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"2\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/e7ca63aea2683535.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"3\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/fdb2722c82ebf619.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],\"$L1e\"]}],{\"children\":[\"(pages)\",[\"$\",\"$1b\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/14c8222b4e6bfb9e.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/f574d745d6edfb67.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"2\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/b1cf855db6ac51b1.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"3\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/ec76de1699f3bb1c.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"4\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/ca44753475331c48.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"5\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/e4938badb52137ae.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],\"$L1f\"]}],{\"children\":[\"docs\",[\"$\",\"$1b\",\"c\",{\"children\":[null,[\"$\",\"$L1c\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"(frontend)\",\"children\",\"(pages)\",\"children\",\"docs\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L1d\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]]}],{\"children\":[[\"topic\",\"configuration\",\"d\"],[\"$\",\"$1b\",\"c\",{\"children\":[null,[\"$\",\"$L1c\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"(frontend)\",\"children\",\"(pages)\",\"children\",\"docs\",\"children\",\"$0:f:0:1:2:children:2:children:2:children:2:children:0\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L1d\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]]}],{\"children\":[[\"doc\",\"localization\",\"d\"],[\"$\",\"$1b\",\"c\",{\"children\":[null,[\"$\",\"$L1c\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"(frontend)\",\"children\",\"(pages)\",\"children\",\"docs\",\"children\",\"$0:f:0:1:2:children:2:children:2:children:2:children:0\",\"children\",\"$0:f:0:1:2:children:2:children:2:children:2:children:2:children:0\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L1d\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}]]}],{\"children\":[\"__PAGE__\",[\"$\",\"$1b\",\"c\",{\"children\":[\"$L20\",[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/3335bd7359ecbb8a.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/26e2d81bd32c127c.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"2\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/fe14b9b58f5d10c1.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"3\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/972e81b151cbc342.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"link\",\"4\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/add6d6451a368571.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]],[\"$\",\"$L21\",null,{\"children\":\"$L22\"}]]}],{},null]},null]},null]},null]},null]},null]},null],[\"$\",\"$1b\",\"h\",{\"children\":[null,[\"$\",\"$1b\",\"GvzCwe3C8Ihwj4ogr0gS4\",{\"children\":[[\"$\",\"$L23\",null,{\"children\":\"$L24\"}],[\"$\",\"$L25\",null,{\"children\":\"$L26\"}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\"}]]}]]}]]],\"m\":\"$undefined\",\"G\":[\"$27\",[]],\"s\":false,\"S\":true}\n"])</script><script>self.__next_f.push([1,"28:I[23558,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"6137\",\"static/chunks/6137-3a61cc21a88452a0.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"9559\",\"static/chunks/app/(frontend)/layout-7e0ce4c8601538b2.js\"],\"PrivacyProvider\"]\n29:I[82688,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"6137\",\"static/chunks/6137-3a61cc21a88452a0.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"9559\",\"static/chunks/app/(frontend)/layout-7e0ce4c8601538b2.js\"],\"GoogleAnalytics\"]\n2a:I[18703,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"6137\",\"static/chunks/6137-3a61cc21a88452a0.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"9559\",\"static/chunks/app/(frontend)/layout-7e0ce4c8601538b2.js\"],\"GoogleTagManager\"]\n2b:I[99154,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"6137\",\"static/chunks/6137-3a61cc21a88452a0.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"9559\",\"static/chunks/app/(frontend)/layout-7e0ce4c8601538b2.js\"],\"Providers\"]\n2c:I[26228,[\"6229\",\"static/chunks/app/(frontend)/error-93d8997970acc7c2.js\"],\"default\"]\n2e:I[47252,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"6137\",\"static/chunks/6137-3a61cc21a88452a0.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"9559\",\"static/chunks/app/(frontend)/layout-7e0ce4c8601538b2.js\"],\"PrivacyBanner\"]\n1e:[\"$\",\"html\",null,{\"lang\":\"en\",\"children"])</script><script>self.__next_f.push([1,"\":[\"$\",\"$L28\",null,{\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"link\",null,{\"rel\":\"icon\",\"href\":\"/images/favicon.svg\"}],[\"$\",\"link\",null,{\"rel\":\"dns-prefetch\",\"href\":\"https://cloud-api.payloadcms.com\"}],[\"$\",\"link\",null,{\"rel\":\"dns-prefetch\",\"href\":\"https://api.github.com/repos/payloadcms/payload\"}],[\"$\",\"link\",null,{\"rel\":\"stylesheet\",\"href\":\"https://cdn.jsdelivr.net/npm/@docsearch/css@3\"}],[\"$\",\"link\",null,{\"rel\":\"preconnect\",\"href\":\"https://www.googletagmanager.com\"}],[\"$\",\"link\",null,{\"rel\":\"preconnect\",\"href\":\"https://www.google-analytics.com\"}],[\"$\",\"$L29\",null,{}]]}],[\"$\",\"body\",null,{\"className\":\"__variable_c1e5c9 __variable_b25fb2\",\"children\":[[\"$\",\"$L2a\",null,{}],[\"$\",\"$L2b\",null,{\"children\":[[\"$\",\"$L1c\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"(frontend)\",\"children\"],\"error\":\"$2c\",\"errorStyles\":[[\"$\",\"link\",\"style-0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/14c8222b4e6bfb9e.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],\"errorScripts\":[],\"template\":[\"$\",\"$L1d\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$L2d\",\"notFoundStyles\":[[\"$\",\"link\",\"style-0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/14c8222b4e6bfb9e.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"style-1\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/e4938badb52137ae.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"style-2\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/f574d745d6edfb67.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"style-3\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/b1cf855db6ac51b1.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"style-4\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/ec76de1699f3bb1c.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"link\",\"style-5\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/ca44753475331c48.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]]}],[\"$\",\"$L2e\",null,{}]]}]]}]]}]}]\n"])</script><script>self.__next_f.push([1,"2f:I[93685,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"9393\",\"static/chunks/9393-f5cd841433221a01.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"8052\",\"static/chunks/8052-dbdea2ef46226629.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"4139\",\"static/chunks/4139-fca05c995471e50e.js\",\"6579\",\"static/chunks/6579-b0979af465cec810.js\",\"9055\",\"static/chunks/app/(frontend)/(pages)/layout-aa781834d55c1303.js\"],\"Header\"]\n30:I[75927,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"9393\",\"static/chunks/9393-f5cd841433221a01.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"8052\",\"static/chunks/8052-dbdea2ef46226629.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"4139\",\"static/chunks/4139-fca05c995471e50e.js\",\"6579\",\"static/chunks/6579-b0979af465cec810.js\",\"9055\",\"static/chunks/app/(frontend)/(pages)/layout-aa781834d55c1303.js\"],\"Footer\"]\n"])</script><script>self.__next_f.push([1,"1f:[[\"$\",\"$L2f\",null,{\"navItems\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"value\":\"6429f73faea011d1b4735131\",\"relationTo\":\"pages\"},\"label\":\"Cloud Pricing\"},\"id\":\"642ac843c802f0015b226392\"},{\"link\":{\"type\":\"custom\",\"url\":\"/docs/getting-started/what-is-payload\",\"label\":\"Docs\"},\"id\":\"6356eb9f28e48683b280c31a\"},{\"link\":{\"type\":\"custom\",\"url\":\"/for-enterprise\",\"label\":\"For Enterprise\"},\"id\":\"6356ebdb28e48683b280c31c\"},{\"link\":{\"type\":\"custom\",\"reference\":{\"value\":\"6363f57fe2d7a415f906bf57\",\"relationTo\":\"pages\"},\"url\":\"/community-help\",\"label\":\"Community Help\"},\"id\":\"637112ee4759ab4f6d59135b\"},{\"link\":{\"type\":\"custom\",\"url\":\"/blog\",\"label\":\"Blog\"},\"id\":\"6356ebee28e48683b280c31d\"}],\"globalType\":\"main-menu\",\"createdAt\":\"2022-10-24T19:47:08.352Z\",\"updatedAt\":\"2024-11-21T01:50:16.754Z\",\"tabs\":[{\"label\":\"Product\",\"enableDirectLink\":true,\"enableDropdown\":true,\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65dd43d561b3c803918e03d4\",\"title\":\"Use Cases\",\"slug\":\"use-cases\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8db67e341109bc09e45\"}]}}},\"description\":\"Simplify your stack and build anything. Or everything.\",\"descriptionLinks\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6362714c8500b86c17b16b78\",\"title\":\"Talk to us\",\"slug\":\"talk-to-us\",\"breadcrumbs\":[{\"doc\":\"6362714c8500b86c17b16b78\",\"url\":\"/talk-to-us\",\"label\":\"Talk to us\",\"id\":\"6740b4f18939b700031acbe9\"}]}},\"label\":\"Schedule a Demo\"},\"id\":\"672258550fd3ba0003029939\"}],\"navItems\":[{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aecafefa327a41786d6431\",\"title\":\"Headless CMS\",\"slug\":\"headless-cms\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dc67e341109bc09e55\"},{\"doc\":\"65aecafefa327a41786d6431\",\"url\":\"/use-cases/headless-cms\",\"label\":\"Headless CMS\",\"id\":\"66e9a8dc67e341109bc09e56\"}]}},\"label\":\"Content Management System\"},\"description\":\"Create with a minimal, powerful editing experience. Extend effortlessly.\"},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"USE CASES\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aecafefa327a41786d6431\",\"title\":\"Headless CMS\",\"slug\":\"headless-cms\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dc67e341109bc09e55\"},{\"doc\":\"65aecafefa327a41786d6431\",\"url\":\"/use-cases/headless-cms\",\"label\":\"Headless CMS\",\"id\":\"66e9a8dc67e341109bc09e56\"}]}},\"label\":\"Headless CMS\"},\"id\":\"67225603fdb03a0003f7b445\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65d8c8e5fa0a8be86415cde0\",\"title\":\"Enterprise App Builder \",\"slug\":\"enterprise-app-builder\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8db67e341109bc09e4f\"},{\"doc\":\"65d8c8e5fa0a8be86415cde0\",\"url\":\"/use-cases/enterprise-app-builder\",\"label\":\"Enterprise App Builder \",\"id\":\"66e9a8db67e341109bc09e50\"}]}},\"label\":\"Enterprise App Builder\"},\"id\":\"67225638fdb03a0003f7b446\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6515c182679a6536704e5988\",\"title\":\"Headless E-commerce\",\"slug\":\"headless-ecommerce\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dd67e341109bc09e67\"},{\"doc\":\"6515c182679a6536704e5988\",\"url\":\"/use-cases/headless-ecommerce\",\"label\":\"Headless E-commerce\",\"id\":\"66e9a8dd67e341109bc09e68\"}]}},\"label\":\"Headless E-Commerce\"},\"id\":\"6722565dfdb03a0003f7b447\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65d78062fa286280ee430772\",\"title\":\"Digital Asset Management\",\"slug\":\"digital-asset-management\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"673b8d00f2827c000311de9e\"},{\"doc\":\"65d78062fa286280ee430772\",\"url\":\"/use-cases/digital-asset-management\",\"label\":\"Digital Asset Management\",\"id\":\"673b8d00f2827c000311de9f\"}]}},\"label\":\"Digital Asset Management\"},\"id\":\"6722566efdb03a0003f7b448\"}]},\"id\":\"65d4d906e9fe56cf2b5f32f7\"},{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"FEATURES\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66be1d5326cd4f261a72ade4\",\"title\":\"Multi-tenancy\",\"slug\":\"multi-tenancy\",\"breadcrumbs\":[{\"doc\":\"66be1d5326cd4f261a72ade4\",\"url\":\"/multi-tenancy\",\"label\":\"Multi-tenancy\",\"id\":\"66e9a8da67e341109bc09e23\"}]}},\"label\":\"Multi-Tenancy\"},\"id\":\"672256c2fdb03a0003f7b44a\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66c8e45afc7a62aa2ce38505\",\"title\":\"White Label Admin Panel\",\"slug\":\"white-label-cms-admin-panel\",\"breadcrumbs\":[{\"doc\":\"66c8e45afc7a62aa2ce38505\",\"url\":\"/white-label-cms-admin-panel\",\"label\":\"White Label Admin Panel\",\"id\":\"672259e92fd2fe000a2a9d03\"}]}},\"label\":\"White Label\"},\"id\":\"672256ccfdb03a0003f7b44b\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65f86069f97c9d4dc46ef767\",\"title\":\"Localization\",\"slug\":\"localization\",\"breadcrumbs\":[{\"doc\":\"65f86069f97c9d4dc46ef767\",\"url\":\"/localization\",\"label\":\"Localization\",\"id\":\"6735081af8404100038b9987\"}]}},\"label\":\"Localization\"},\"id\":\"672256e7fdb03a0003f7b44c\"},{\"link\":{\"type\":\"custom\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636d859b0e6e763e9a571056\",\"title\":\"Terms\",\"slug\":\"terms\",\"breadcrumbs\":[{\"doc\":\"636d859b0e6e763e9a571056\",\"url\":\"/terms\",\"label\":\"Terms\",\"id\":\"66e9a8de67e341109bc09e8b\"}]}},\"url\":\"/access-control\",\"label\":\"Access Control\"},\"id\":\"67225708fdb03a0003f7b44d\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/headless-cms-auth\",\"label\":\"Auth\"},\"id\":\"67225717fdb03a0003f7b44e\"}]},\"id\":\"672256bafdb03a0003f7b449\"},{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"tag\":\"CASE STUDIES\",\"label\":{\"root\":{\"children\":[{\"children\":[{\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"See what others are building with Payload.\",\"type\":\"text\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"heading\",\"version\":1,\"tag\":\"h4\"}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}},\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6363f57fe2d7a415f906bf57\",\"title\":\"Case Studies\",\"slug\":\"case-studies\",\"breadcrumbs\":[{\"doc\":\"6363f57fe2d7a415f906bf57\",\"url\":\"/case-studies\",\"label\":\"Case Studies\",\"id\":\"6734ca4469993200036409d7\"}]}},\"label\":\"Browse Case Studies\"},\"id\":\"672257c10fd3ba0003029938\"}]},\"listLinks\":{\"links\":[]},\"id\":\"672257750fd3ba0003029937\"}],\"id\":\"65d4d889e9fe56cf2b5f32f5\"},{\"label\":\"Why Payload\",\"enableDropdown\":true,\"link\":{\"type\":\"reference\",\"reference\":null},\"description\":\"Build tomorrow’s web with a modern solution you truly own.\",\"descriptionLinks\":[],\"navItems\":[{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66031af2301a1a9f1ff3c12d\",\"title\":\"Marketers\",\"slug\":\"marketers\",\"breadcrumbs\":[{\"doc\":\"66031af2301a1a9f1ff3c12d\",\"url\":\"/marketers\",\"label\":\"Marketers\",\"id\":\"67350acfa60f1300030e913d\"}]}},\"label\":\"For Marketing Teams\"},\"description\":\"Advanced features like Visual Editing and Live Preview are giving a head back to the headless CMS.\"},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"PAYLOAD IS FOR\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aabcf2fa327a417862bb7f\",\"title\":\"Developers\",\"slug\":\"developers\",\"breadcrumbs\":[{\"doc\":\"65aabcf2fa327a417862bb7f\",\"url\":\"/developers\",\"label\":\"Developers\",\"id\":\"6716cda483f57500031ea25f\"}]}},\"label\":\"Developers\"},\"id\":\"672264e62897eb00037a7463\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66031af2301a1a9f1ff3c12d\",\"title\":\"Marketers\",\"slug\":\"marketers\",\"breadcrumbs\":[{\"doc\":\"66031af2301a1a9f1ff3c12d\",\"url\":\"/marketers\",\"label\":\"Marketers\",\"id\":\"67350acfa60f1300030e913d\"}]}},\"label\":\"Marketing teams\"},\"id\":\"672264de2897eb00037a7462\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"64515c36006aeaef3d3d62c1\",\"title\":\"Payload for Enterprise\",\"slug\":\"enterprise\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8dd67e341109bc09e6d\"}]}},\"label\":\"Enterprise companies\"},\"id\":\"672265002897eb00037a7464\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65e62d33ce19cad022049171\",\"title\":\"Become a Payload Partner\",\"slug\":\"become-a-partner\",\"breadcrumbs\":[{\"doc\":\"65e62d33ce19cad022049171\",\"url\":\"/become-a-partner\",\"label\":\"Become a Payload Partner\",\"id\":\"66e9a8da67e341109bc09e35\"}]}},\"label\":\"Agencies \u0026 Consultancies\"},\"id\":\"672265152897eb00037a7465\"}]},\"id\":\"65d4d9fae9fe56cf2b5f32fc\"},{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"COMPARE PAYLOAD\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636e93cfb554c827004b1f72\",\"title\":\"WordPress\",\"slug\":\"wordpress\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"672e3abe8e63e100031239ab\"},{\"doc\":\"636e93cfb554c827004b1f72\",\"url\":\"/compare/wordpress\",\"label\":\"WordPress\",\"id\":\"672e3abe8e63e100031239ac\"}]}},\"label\":\"Payload vs WordPress\"},\"id\":\"672265b99d0bce000373941a\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65de9516c9f1210a9e992745\",\"title\":\"Contentful\",\"slug\":\"contentful\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"66e9a8e067e341109bc09e9a\"},{\"doc\":\"65de9516c9f1210a9e992745\",\"url\":\"/compare/contentful\",\"label\":\"Contentful\",\"id\":\"66e9a8e067e341109bc09e9b\"}]}},\"label\":\"Payload vs Contentful\"},\"id\":\"672265eb9d0bce000373941c\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"660d97a9fac5ba6ebe6586e8\",\"title\":\"Sanity\",\"slug\":\"sanity\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"672e3bfdcc1e340003f57b92\"},{\"doc\":\"660d97a9fac5ba6ebe6586e8\",\"url\":\"/compare/sanity\",\"label\":\"Sanity\",\"id\":\"672e3bfdcc1e340003f57b93\"}]}},\"label\":\"Payload vs Sanity\"},\"id\":\"672265dc2897eb00037a746f\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65de4fbc6fea7b015bd25002\",\"title\":\"Strapi\",\"slug\":\"strapi\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"672e3b8ba16aa800030c6b6d\"},{\"doc\":\"65de4fbc6fea7b015bd25002\",\"url\":\"/compare/strapi\",\"label\":\"Strapi\",\"id\":\"672e3b8ba16aa800030c6b6e\"}]}},\"label\":\"Payload vs Strapi\"},\"id\":\"672265e29d0bce000373941b\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65de401f6fea7b015bd1faaf\",\"title\":\"Directus\",\"slug\":\"directus\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"672e3bc619224a00034cde3d\"},{\"doc\":\"65de401f6fea7b015bd1faaf\",\"url\":\"/compare/directus\",\"label\":\"Directus\",\"id\":\"672e3bc619224a00034cde3e\"}]}},\"label\":\"Payload vs Directus\"},\"id\":\"672265f69d0bce000373941d\"}]},\"id\":\"672265ad9d0bce0003739419\"},{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"tag\":\"AGENCY TESTIMONIAL\",\"label\":{\"root\":{\"children\":[{\"children\":[{\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"\\\"Payload has transformed the way our clients manage content. It's an indispensable tool for any modern agency.\\\"\",\"type\":\"text\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"heading\",\"version\":1,\"tag\":\"h4\"}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}},\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65e62d33ce19cad022049171\",\"title\":\"Become a Payload Partner\",\"slug\":\"become-a-partner\",\"breadcrumbs\":[{\"doc\":\"65e62d33ce19cad022049171\",\"url\":\"/become-a-partner\",\"label\":\"Become a Payload Partner\",\"id\":\"66e9a8da67e341109bc09e35\"}]}},\"label\":\"Become a Partner\"},\"id\":\"6722692b181010000377a55a\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/partners\",\"label\":\"Find a Partner\"},\"id\":\"6722693a181010000377a55b\"}]},\"listLinks\":{\"links\":[]},\"id\":\"672266cde9f80e000998f529\"}],\"id\":\"65d4d9efe9fe56cf2b5f32fb\"},{\"label\":\"Developers\",\"enableDirectLink\":true,\"enableDropdown\":true,\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aabcf2fa327a417862bb7f\",\"title\":\"Developers\",\"slug\":\"developers\",\"breadcrumbs\":[{\"doc\":\"65aabcf2fa327a417862bb7f\",\"url\":\"/developers\",\"label\":\"Developers\",\"id\":\"6716cda483f57500031ea25f\"}]}}},\"description\":\"Code-based nature means you can build on top of it to power anything.\",\"descriptionLinks\":[],\"navItems\":[{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"Resources\",\"links\":[{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/docs\",\"label\":\"Documentation\"},\"id\":\"65e1be3486c23b0764901fb6\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload/tree/main/examples\",\"label\":\"Examples\"},\"id\":\"65e1be5d86c23b0764901fb7\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload/tree/main/templates\",\"label\":\"Templates\"},\"id\":\"65e1be6386c23b0764901fb8\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload\",\"label\":\"GitHub\"},\"id\":\"65e1be8186c23b0764901fb9\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload/releases\",\"label\":\"Releases\"},\"id\":\"673654b5b51317b8269142a8\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/blog\",\"label\":\"Blog\"},\"id\":\"666210b5fb64712933eebd96\"}]},\"id\":\"65e1be2186c23b0764901fb5\"},{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"Community\",\"links\":[{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload/discussions/categories/roadmap\",\"label\":\"Roadmap\"},\"id\":\"65e1bf4186c23b0764901fbb\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://discord.com/invite/r6sCXqVk3v\",\"label\":\"Discord\"},\"id\":\"65e1bf5386c23b0764901fbc\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/community-help\",\"label\":\"Community Help\"},\"id\":\"65e1bf6186c23b0764901fbd\"}]},\"id\":\"65e1bedc86c23b0764901fba\"},{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"tag\":\"Payload Cloud\",\"label\":{\"root\":{\"type\":\"root\",\"children\":[{\"type\":\"heading\",\"children\":[{\"type\":\"text\",\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"Deploy your entire stack in one place with Payload Cloud.\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"tag\":\"h4\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"version\":1}},\"links\":[{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/login\",\"label\":\"Login\"},\"id\":\"65e1c02b86c23b0764901fc0\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6429f73faea011d1b4735131\",\"title\":\"Payload Cloud Pricing\",\"slug\":\"cloud-pricing\",\"breadcrumbs\":[{\"doc\":\"6429f73faea011d1b4735131\",\"url\":\"/cloud-pricing\",\"label\":\"Payload Cloud Pricing\",\"id\":\"6733b66228675e00035f99e1\"}]}},\"label\":\"Cloud Pricing\"},\"id\":\"65e1c03686c23b0764901fc1\"}]},\"listLinks\":{\"links\":[]},\"id\":\"65e1c00b86c23b0764901fbf\"}],\"id\":\"65e1be0386c23b0764901fb4\"},{\"label\":\"Enterprise\",\"enableDirectLink\":true,\"enableDropdown\":true,\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"64515c36006aeaef3d3d62c1\",\"title\":\"Payload for Enterprise\",\"slug\":\"enterprise\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8dd67e341109bc09e6d\"}]}},\"url\":\"/enterpr\"},\"description\":\"It’s time to take back your content infrastructure.\",\"descriptionLinks\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6362714c8500b86c17b16b78\",\"title\":\"Talk to us\",\"slug\":\"talk-to-us\",\"breadcrumbs\":[{\"doc\":\"6362714c8500b86c17b16b78\",\"url\":\"/talk-to-us\",\"label\":\"Talk to us\",\"id\":\"6740b4f18939b700031acbe9\"}]}},\"url\":\"https://demo.payloadcms.com/\",\"label\":\"Schedule a Demo\"},\"id\":\"65d51d0e79b580a77c5b6227\"}],\"navItems\":[{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"Enterprise Features\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6582f8742693ab4da8ed6601\",\"title\":\"Single Sign-On (SSO)\",\"slug\":\"single-sign-on-sso\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8de67e341109bc09e78\"},{\"doc\":\"6582f8742693ab4da8ed6601\",\"url\":\"/enterprise/single-sign-on-sso\",\"label\":\"Single Sign-On (SSO)\",\"id\":\"66e9a8de67e341109bc09e79\"}]}},\"label\":\"SSO\"},\"id\":\"65efaf38e920e8aba905be5e\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"67095b76a670b2b9de919cda\",\"title\":\"AI Auto-Embedding\",\"slug\":\"ai-framework\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"6740c888520e8e00034be568\"},{\"doc\":\"67095b76a670b2b9de919cda\",\"url\":\"/enterprise/ai-framework\",\"label\":\"AI Auto-Embedding\",\"id\":\"6740c888520e8e00034be569\"}]}},\"label\":\"AI Auto-Embedding\"},\"id\":\"673e91b8e9c9674bce3541a1\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6580ab1a2693ab4da8e670cf\",\"title\":\"Publishing Workflows\",\"slug\":\"publishing-workflows\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8de67e341109bc09e7a\"},{\"doc\":\"6580ab1a2693ab4da8e670cf\",\"url\":\"/enterprise/publishing-workflows\",\"label\":\"Publishing Workflows\",\"id\":\"66e9a8de67e341109bc09e7b\"}]}},\"label\":\"Publishing Workflows\"},\"id\":\"65efaf43e920e8aba905be5f\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6582fb422693ab4da8ed728c\",\"title\":\"Visual Editor\",\"slug\":\"visual-editor\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"673b8cc0f2827c000311de9c\"},{\"doc\":\"6582fb422693ab4da8ed728c\",\"url\":\"/enterprise/visual-editor\",\"label\":\"Visual Editor\",\"id\":\"673b8cc0f2827c000311de9d\"}]}},\"label\":\"Visual Editor\"},\"id\":\"65efaf53e920e8aba905be60\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66674dbf5fdf174caf28af9a\",\"title\":\"A/B Testing\",\"slug\":\"headless-ab-variant-testing\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8de67e341109bc09e7c\"},{\"doc\":\"66674dbf5fdf174caf28af9a\",\"url\":\"/enterprise/headless-ab-variant-testing\",\"label\":\"A/B Testing\",\"id\":\"66e9a8de67e341109bc09e7d\"}]}},\"label\":\"Static A/B testing\"},\"id\":\"65efaf7de920e8aba905be62\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6585ae1c50262a9de087c197\",\"title\":\"Enterprise AI\",\"slug\":\"enterprise-ai\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"673b8e1838406000039bc278\"},{\"doc\":\"6585ae1c50262a9de087c197\",\"url\":\"/enterprise/enterprise-ai\",\"label\":\"Enterprise AI\",\"id\":\"673b8e1838406000039bc279\"}]}},\"label\":\"AI features\"},\"id\":\"65efaf69e920e8aba905be61\"}]},\"id\":\"65d51d3479b580a77c5b6229\"},{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"Customer Stories\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"65df959d5412b96c3774b300\",\"title\":\"Microsoft\",\"featuredImage\":\"65e20b6b9a2904b291d8d6b0\",\"slug\":\"microsoft\",\"url\":\"https://create.microsoft.com/en-us/learn\"}},\"url\":\"\",\"label\":\"Microsoft\"},\"id\":\"65d51dac79b580a77c5b6231\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"663e5ff317e86af24a14667f\",\"title\":\"Blue Origin\",\"featuredImage\":\"664cdff8f15c266ea3f35c62\",\"slug\":\"blue-origin-club-for-the-future\",\"url\":\"https://www.clubforfuture.org/postcards\"}},\"label\":\"Blue Origin\"},\"id\":\"6656363f8f0ef0f7d812334a\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"65df85de5412b96c3774a72b\",\"title\":\"Hello Bello\",\"featuredImage\":\"65df923b5412b96c3774b1f5\",\"slug\":\"hello-bello\",\"url\":\"https://hellobello.com\"}},\"label\":\"Hello Bello\"},\"id\":\"65d51dbb79b580a77c5b6232\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"66198b47d5974b13de0837e5\",\"title\":\"Mythical Society\",\"featuredImage\":\"66282250755bf549f20ac73c\",\"slug\":\"mythical-society\",\"url\":\"https://www.mythicalsociety.com/\"}},\"label\":\"Mythical Society\"},\"id\":\"65d51de079b580a77c5b6235\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"65df9dd27f8635bf2efc401f\",\"title\":\"Tekton\",\"featuredImage\":\"65de401b6fea7b015bd1fa80\",\"slug\":\"tekton\",\"url\":\"https://tekton.com\"}},\"label\":\"Tekton\"},\"id\":\"65e22884df88b478a1e6db58\"}]},\"id\":\"65d51d9d79b580a77c5b6230\"},{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"tag\":\"Featured Customer Story\",\"label\":{\"root\":{\"type\":\"root\",\"children\":[{\"type\":\"heading\",\"children\":[{\"type\":\"text\",\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"Microsoft chose Payload to tell the world about AI.\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"tag\":\"h4\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"version\":1}},\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"65df959d5412b96c3774b300\",\"title\":\"Microsoft\",\"featuredImage\":\"65e20b6b9a2904b291d8d6b0\",\"slug\":\"microsoft\",\"url\":\"https://create.microsoft.com/en-us/learn\"}},\"label\":\"Read the case study\"},\"id\":\"65d51e0c79b580a77c5b6237\"}]},\"listLinks\":{\"links\":[]},\"id\":\"65d51df579b580a77c5b6236\"}],\"id\":\"65d51ce379b580a77c5b6226\"},{\"label\":\"Docs\",\"enableDirectLink\":true,\"link\":{\"type\":\"custom\",\"newTab\":false,\"reference\":null,\"url\":\"/docs\"},\"descriptionLinks\":[],\"navItems\":[{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"https://github.com/payloadcms/payload\",\"label\":\"GitHub\"}},\"featuredLink\":{\"links\":[{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"https://github.com/payloadcms/payload\",\"label\":\"GitHub\"},\"id\":\"65d55a9dfa96b45f5e85af05\"}]},\"listLinks\":{\"links\":[]},\"id\":\"65d559a4a4b1c51621f5bbdb\"}],\"id\":\"65d5599ca4b1c51621f5bbda\"}],\"menuCta\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/get-started\",\"label\":\"Get Started\"},\"id\":\"6356ebbc6ad5ce88fb581531\"}],[\"$\",\"div\",null,{\"children\":[[\"$\",\"$L1c\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"(frontend)\",\"children\",\"(pages)\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L1d\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\"}],[\"$\",\"div\",null,{\"id\":\"docsearch\"}],[\"$\",\"$L30\",null,{\"columns\":[{\"label\":\"Use Cases\",\"navItems\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aecafefa327a41786d6431\",\"title\":\"Headless CMS\",\"slug\":\"headless-cms\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dc67e341109bc09e55\"},{\"doc\":\"65aecafefa327a41786d6431\",\"url\":\"/use-cases/headless-cms\",\"label\":\"Headless CMS\",\"id\":\"66e9a8dc67e341109bc09e56\"}]}},\"label\":\"Content Management System\"},\"id\":\"65e09c6139c8d261526eba36\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65d8c8e5fa0a8be86415cde0\",\"title\":\"Enterprise App Builder \",\"slug\":\"enterprise-app-builder\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8db67e341109bc09e4f\"},{\"doc\":\"65d8c8e5fa0a8be86415cde0\",\"url\":\"/use-cases/enterprise-app-builder\",\"label\":\"Enterprise App Builder \",\"id\":\"66e9a8db67e341109bc09e50\"}]}},\"label\":\"Enterprise App Builder\"},\"id\":\"65e09c7139c8d261526eba37\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6515c182679a6536704e5988\",\"title\":\"Headless E-commerce\",\"slug\":\"headless-ecommerce\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dd67e341109bc09e67\"},{\"doc\":\"6515c182679a6536704e5988\",\"url\":\"/use-cases/headless-ecommerce\",\"label\":\"Headless E-commerce\",\"id\":\"66e9a8dd67e341109bc09e68\"}]}},\"label\":\"Headless E-Commerce\"},\"id\":\"65e09c7e39c8d261526eba38\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65d78062fa286280ee430772\",\"title\":\"Digital Asset Management\",\"slug\":\"digital-asset-management\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"673b8d00f2827c000311de9e\"},{\"doc\":\"65d78062fa286280ee430772\",\"url\":\"/use-cases/digital-asset-management\",\"label\":\"Digital Asset Management\",\"id\":\"673b8d00f2827c000311de9f\"}]}},\"label\":\"Digital Asset Management\"},\"id\":\"65e09c8c39c8d261526eba39\"}],\"id\":\"65e09c5f39c8d261526eba35\"},{\"label\":\"Developers\",\"navItems\":[{\"link\":{\"type\":\"custom\",\"reference\":{\"relationTo\":\"pages\",\"value\":\"636c1a8b74e94056f1fada16\"},\"url\":\"https://payloadcms.com/new\",\"label\":\"Payload Cloud\"},\"id\":\"65e09c9f39c8d261526eba3b\"},{\"link\":{\"type\":\"custom\",\"url\":\"/docs/getting-started/what-is-payload\",\"label\":\"Documentation\",\"reference\":null},\"id\":\"65e09cb639c8d261526eba3c\"},{\"link\":{\"type\":\"custom\",\"url\":\"/community-help\",\"label\":\"Community Help\",\"reference\":null},\"id\":\"65e09cd839c8d261526eba3d\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"url\":\"https://github.com/payloadcms/payload/discussions/categories/roadmap?discussions_q=category%3ARoadmap+\",\"label\":\"Roadmap\",\"reference\":null},\"id\":\"65e09cea39c8d261526eba3e\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"url\":\"https://github.com/payloadcms/payload/tree/main/templates\",\"label\":\"Templates\",\"reference\":null},\"id\":\"65e09d3039c8d261526eba3f\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"url\":\"https://github.com/payloadcms/payload/releases\",\"label\":\"Releases\",\"reference\":null},\"id\":\"6736575db51317b8269142a9\"}],\"id\":\"65e09c9939c8d261526eba3a\"},{\"label\":\"Company\",\"navItems\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6429f73faea011d1b4735131\",\"title\":\"Payload Cloud Pricing\",\"slug\":\"cloud-pricing\",\"breadcrumbs\":[{\"doc\":\"6429f73faea011d1b4735131\",\"url\":\"/cloud-pricing\",\"label\":\"Payload Cloud Pricing\",\"id\":\"6733b66228675e00035f99e1\"}]}},\"label\":\"Pricing\"},\"id\":\"65e09d5e39c8d261526eba42\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6362714c8500b86c17b16b78\",\"title\":\"Talk to us\",\"slug\":\"talk-to-us\",\"breadcrumbs\":[{\"doc\":\"6362714c8500b86c17b16b78\",\"url\":\"/talk-to-us\",\"label\":\"Talk to us\",\"id\":\"6740b4f18939b700031acbe9\"}]}},\"label\":\"Enterprise\"},\"id\":\"65e09d7239c8d261526eba43\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6363f57fe2d7a415f906bf57\",\"title\":\"Case Studies\",\"slug\":\"case-studies\",\"breadcrumbs\":[{\"doc\":\"6363f57fe2d7a415f906bf57\",\"url\":\"/case-studies\",\"label\":\"Case Studies\",\"id\":\"6734ca4469993200036409d7\"}]}},\"label\":\"Case Studies\"},\"id\":\"65e09d9239c8d261526eba46\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65e62d33ce19cad022049171\",\"title\":\"Become a Payload Partner\",\"slug\":\"become-a-partner\",\"breadcrumbs\":[{\"doc\":\"65e62d33ce19cad022049171\",\"url\":\"/become-a-partner\",\"label\":\"Become a Payload Partner\",\"id\":\"66e9a8da67e341109bc09e35\"}]}},\"label\":\"Partner With Us\"},\"id\":\"65e09d7e39c8d261526eba44\"},{\"link\":{\"type\":\"custom\",\"url\":\"/partners\",\"label\":\"Find a Partner\",\"reference\":null},\"id\":\"66d0912d92e684b1f3e1bed9\"},{\"link\":{\"type\":\"custom\",\"url\":\"/blog\",\"label\":\"Blog\",\"reference\":null},\"id\":\"65e09d9b39c8d261526eba47\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65b1beb3fa327a417876b63d\",\"title\":\"Security\",\"slug\":\"security\",\"breadcrumbs\":[{\"doc\":\"65b1beb3fa327a417876b63d\",\"url\":\"/security\",\"label\":\"Security\",\"id\":\"66e9a8dc67e341109bc09e53\"}]}},\"label\":\"Security\"},\"id\":\"65e76c53590a277ae5ad8cbb\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636d5408969bcf4617bd7971\",\"title\":\"Compare\",\"slug\":\"compare\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"66e9a8df67e341109bc09e8d\"}]}},\"label\":\"Compare Payload\"},\"id\":\"65e09d8939c8d261526eba45\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636d859b0e6e763e9a571056\",\"title\":\"Terms\",\"slug\":\"terms\",\"breadcrumbs\":[{\"doc\":\"636d859b0e6e763e9a571056\",\"url\":\"/terms\",\"label\":\"Terms\",\"id\":\"66e9a8de67e341109bc09e8b\"}]}},\"url\":\"\",\"label\":\"Terms of Service\"},\"id\":\"65e09da839c8d261526eba48\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/privacy\",\"label\":\"Privacy Policy\"},\"id\":\"65e09db439c8d261526eba49\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636d818f0e6e763e9a56f09d\",\"title\":\"Contact\",\"slug\":\"contact\",\"breadcrumbs\":[{\"doc\":\"636d818f0e6e763e9a56f09d\",\"url\":\"/contact\",\"label\":\"Contact\",\"id\":\"66e9a8de67e341109bc09e8c\"}]}},\"label\":\"Contact\"},\"id\":\"65e8e2887a897c4ca1a59be5\"}],\"id\":\"65e09d5839c8d261526eba41\"}],\"globalType\":\"footer\",\"createdAt\":\"2022-11-04T18:46:31.302Z\",\"updatedAt\":\"2024-11-14T20:02:58.886Z\",\"id\":\"63655e0793374a39945c71e5\"}]]}]]\n"])</script><script>self.__next_f.push([1,"31:I[31279,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"9393\",\"static/chunks/9393-f5cd841433221a01.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"8052\",\"static/chunks/8052-dbdea2ef46226629.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"4139\",\"static/chunks/4139-fca05c995471e50e.js\",\"6579\",\"static/chunks/6579-b0979af465cec810.js\",\"947\",\"static/chunks/app/(frontend)/not-found-80590df17b7d6f8e.js\"],\"ErrorMessage\"]\n"])</script><script>self.__next_f.push([1,"2d:[[\"$\",\"$L2f\",null,{\"navItems\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"value\":\"6429f73faea011d1b4735131\",\"relationTo\":\"pages\"},\"label\":\"Cloud Pricing\"},\"id\":\"642ac843c802f0015b226392\"},{\"link\":{\"type\":\"custom\",\"url\":\"/docs/getting-started/what-is-payload\",\"label\":\"Docs\"},\"id\":\"6356eb9f28e48683b280c31a\"},{\"link\":{\"type\":\"custom\",\"url\":\"/for-enterprise\",\"label\":\"For Enterprise\"},\"id\":\"6356ebdb28e48683b280c31c\"},{\"link\":{\"type\":\"custom\",\"reference\":{\"value\":\"6363f57fe2d7a415f906bf57\",\"relationTo\":\"pages\"},\"url\":\"/community-help\",\"label\":\"Community Help\"},\"id\":\"637112ee4759ab4f6d59135b\"},{\"link\":{\"type\":\"custom\",\"url\":\"/blog\",\"label\":\"Blog\"},\"id\":\"6356ebee28e48683b280c31d\"}],\"globalType\":\"main-menu\",\"createdAt\":\"2022-10-24T19:47:08.352Z\",\"updatedAt\":\"2024-11-21T01:50:16.754Z\",\"tabs\":[{\"label\":\"Product\",\"enableDirectLink\":true,\"enableDropdown\":true,\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65dd43d561b3c803918e03d4\",\"title\":\"Use Cases\",\"slug\":\"use-cases\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8db67e341109bc09e45\"}]}}},\"description\":\"Simplify your stack and build anything. Or everything.\",\"descriptionLinks\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6362714c8500b86c17b16b78\",\"title\":\"Talk to us\",\"slug\":\"talk-to-us\",\"breadcrumbs\":[{\"doc\":\"6362714c8500b86c17b16b78\",\"url\":\"/talk-to-us\",\"label\":\"Talk to us\",\"id\":\"6740b4f18939b700031acbe9\"}]}},\"label\":\"Schedule a Demo\"},\"id\":\"672258550fd3ba0003029939\"}],\"navItems\":[{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aecafefa327a41786d6431\",\"title\":\"Headless CMS\",\"slug\":\"headless-cms\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dc67e341109bc09e55\"},{\"doc\":\"65aecafefa327a41786d6431\",\"url\":\"/use-cases/headless-cms\",\"label\":\"Headless CMS\",\"id\":\"66e9a8dc67e341109bc09e56\"}]}},\"label\":\"Content Management System\"},\"description\":\"Create with a minimal, powerful editing experience. Extend effortlessly.\"},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"USE CASES\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aecafefa327a41786d6431\",\"title\":\"Headless CMS\",\"slug\":\"headless-cms\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dc67e341109bc09e55\"},{\"doc\":\"65aecafefa327a41786d6431\",\"url\":\"/use-cases/headless-cms\",\"label\":\"Headless CMS\",\"id\":\"66e9a8dc67e341109bc09e56\"}]}},\"label\":\"Headless CMS\"},\"id\":\"67225603fdb03a0003f7b445\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65d8c8e5fa0a8be86415cde0\",\"title\":\"Enterprise App Builder \",\"slug\":\"enterprise-app-builder\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8db67e341109bc09e4f\"},{\"doc\":\"65d8c8e5fa0a8be86415cde0\",\"url\":\"/use-cases/enterprise-app-builder\",\"label\":\"Enterprise App Builder \",\"id\":\"66e9a8db67e341109bc09e50\"}]}},\"label\":\"Enterprise App Builder\"},\"id\":\"67225638fdb03a0003f7b446\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6515c182679a6536704e5988\",\"title\":\"Headless E-commerce\",\"slug\":\"headless-ecommerce\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dd67e341109bc09e67\"},{\"doc\":\"6515c182679a6536704e5988\",\"url\":\"/use-cases/headless-ecommerce\",\"label\":\"Headless E-commerce\",\"id\":\"66e9a8dd67e341109bc09e68\"}]}},\"label\":\"Headless E-Commerce\"},\"id\":\"6722565dfdb03a0003f7b447\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65d78062fa286280ee430772\",\"title\":\"Digital Asset Management\",\"slug\":\"digital-asset-management\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"673b8d00f2827c000311de9e\"},{\"doc\":\"65d78062fa286280ee430772\",\"url\":\"/use-cases/digital-asset-management\",\"label\":\"Digital Asset Management\",\"id\":\"673b8d00f2827c000311de9f\"}]}},\"label\":\"Digital Asset Management\"},\"id\":\"6722566efdb03a0003f7b448\"}]},\"id\":\"65d4d906e9fe56cf2b5f32f7\"},{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"FEATURES\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66be1d5326cd4f261a72ade4\",\"title\":\"Multi-tenancy\",\"slug\":\"multi-tenancy\",\"breadcrumbs\":[{\"doc\":\"66be1d5326cd4f261a72ade4\",\"url\":\"/multi-tenancy\",\"label\":\"Multi-tenancy\",\"id\":\"66e9a8da67e341109bc09e23\"}]}},\"label\":\"Multi-Tenancy\"},\"id\":\"672256c2fdb03a0003f7b44a\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66c8e45afc7a62aa2ce38505\",\"title\":\"White Label Admin Panel\",\"slug\":\"white-label-cms-admin-panel\",\"breadcrumbs\":[{\"doc\":\"66c8e45afc7a62aa2ce38505\",\"url\":\"/white-label-cms-admin-panel\",\"label\":\"White Label Admin Panel\",\"id\":\"672259e92fd2fe000a2a9d03\"}]}},\"label\":\"White Label\"},\"id\":\"672256ccfdb03a0003f7b44b\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65f86069f97c9d4dc46ef767\",\"title\":\"Localization\",\"slug\":\"localization\",\"breadcrumbs\":[{\"doc\":\"65f86069f97c9d4dc46ef767\",\"url\":\"/localization\",\"label\":\"Localization\",\"id\":\"6735081af8404100038b9987\"}]}},\"label\":\"Localization\"},\"id\":\"672256e7fdb03a0003f7b44c\"},{\"link\":{\"type\":\"custom\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636d859b0e6e763e9a571056\",\"title\":\"Terms\",\"slug\":\"terms\",\"breadcrumbs\":[{\"doc\":\"636d859b0e6e763e9a571056\",\"url\":\"/terms\",\"label\":\"Terms\",\"id\":\"66e9a8de67e341109bc09e8b\"}]}},\"url\":\"/access-control\",\"label\":\"Access Control\"},\"id\":\"67225708fdb03a0003f7b44d\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/headless-cms-auth\",\"label\":\"Auth\"},\"id\":\"67225717fdb03a0003f7b44e\"}]},\"id\":\"672256bafdb03a0003f7b449\"},{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"tag\":\"CASE STUDIES\",\"label\":{\"root\":{\"children\":[{\"children\":[{\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"See what others are building with Payload.\",\"type\":\"text\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"heading\",\"version\":1,\"tag\":\"h4\"}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}},\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6363f57fe2d7a415f906bf57\",\"title\":\"Case Studies\",\"slug\":\"case-studies\",\"breadcrumbs\":[{\"doc\":\"6363f57fe2d7a415f906bf57\",\"url\":\"/case-studies\",\"label\":\"Case Studies\",\"id\":\"6734ca4469993200036409d7\"}]}},\"label\":\"Browse Case Studies\"},\"id\":\"672257c10fd3ba0003029938\"}]},\"listLinks\":{\"links\":[]},\"id\":\"672257750fd3ba0003029937\"}],\"id\":\"65d4d889e9fe56cf2b5f32f5\"},{\"label\":\"Why Payload\",\"enableDropdown\":true,\"link\":{\"type\":\"reference\",\"reference\":null},\"description\":\"Build tomorrow’s web with a modern solution you truly own.\",\"descriptionLinks\":[],\"navItems\":[{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66031af2301a1a9f1ff3c12d\",\"title\":\"Marketers\",\"slug\":\"marketers\",\"breadcrumbs\":[{\"doc\":\"66031af2301a1a9f1ff3c12d\",\"url\":\"/marketers\",\"label\":\"Marketers\",\"id\":\"67350acfa60f1300030e913d\"}]}},\"label\":\"For Marketing Teams\"},\"description\":\"Advanced features like Visual Editing and Live Preview are giving a head back to the headless CMS.\"},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"PAYLOAD IS FOR\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aabcf2fa327a417862bb7f\",\"title\":\"Developers\",\"slug\":\"developers\",\"breadcrumbs\":[{\"doc\":\"65aabcf2fa327a417862bb7f\",\"url\":\"/developers\",\"label\":\"Developers\",\"id\":\"6716cda483f57500031ea25f\"}]}},\"label\":\"Developers\"},\"id\":\"672264e62897eb00037a7463\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66031af2301a1a9f1ff3c12d\",\"title\":\"Marketers\",\"slug\":\"marketers\",\"breadcrumbs\":[{\"doc\":\"66031af2301a1a9f1ff3c12d\",\"url\":\"/marketers\",\"label\":\"Marketers\",\"id\":\"67350acfa60f1300030e913d\"}]}},\"label\":\"Marketing teams\"},\"id\":\"672264de2897eb00037a7462\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"64515c36006aeaef3d3d62c1\",\"title\":\"Payload for Enterprise\",\"slug\":\"enterprise\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8dd67e341109bc09e6d\"}]}},\"label\":\"Enterprise companies\"},\"id\":\"672265002897eb00037a7464\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65e62d33ce19cad022049171\",\"title\":\"Become a Payload Partner\",\"slug\":\"become-a-partner\",\"breadcrumbs\":[{\"doc\":\"65e62d33ce19cad022049171\",\"url\":\"/become-a-partner\",\"label\":\"Become a Payload Partner\",\"id\":\"66e9a8da67e341109bc09e35\"}]}},\"label\":\"Agencies \u0026 Consultancies\"},\"id\":\"672265152897eb00037a7465\"}]},\"id\":\"65d4d9fae9fe56cf2b5f32fc\"},{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"COMPARE PAYLOAD\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636e93cfb554c827004b1f72\",\"title\":\"WordPress\",\"slug\":\"wordpress\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"672e3abe8e63e100031239ab\"},{\"doc\":\"636e93cfb554c827004b1f72\",\"url\":\"/compare/wordpress\",\"label\":\"WordPress\",\"id\":\"672e3abe8e63e100031239ac\"}]}},\"label\":\"Payload vs WordPress\"},\"id\":\"672265b99d0bce000373941a\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65de9516c9f1210a9e992745\",\"title\":\"Contentful\",\"slug\":\"contentful\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"66e9a8e067e341109bc09e9a\"},{\"doc\":\"65de9516c9f1210a9e992745\",\"url\":\"/compare/contentful\",\"label\":\"Contentful\",\"id\":\"66e9a8e067e341109bc09e9b\"}]}},\"label\":\"Payload vs Contentful\"},\"id\":\"672265eb9d0bce000373941c\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"660d97a9fac5ba6ebe6586e8\",\"title\":\"Sanity\",\"slug\":\"sanity\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"672e3bfdcc1e340003f57b92\"},{\"doc\":\"660d97a9fac5ba6ebe6586e8\",\"url\":\"/compare/sanity\",\"label\":\"Sanity\",\"id\":\"672e3bfdcc1e340003f57b93\"}]}},\"label\":\"Payload vs Sanity\"},\"id\":\"672265dc2897eb00037a746f\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65de4fbc6fea7b015bd25002\",\"title\":\"Strapi\",\"slug\":\"strapi\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"672e3b8ba16aa800030c6b6d\"},{\"doc\":\"65de4fbc6fea7b015bd25002\",\"url\":\"/compare/strapi\",\"label\":\"Strapi\",\"id\":\"672e3b8ba16aa800030c6b6e\"}]}},\"label\":\"Payload vs Strapi\"},\"id\":\"672265e29d0bce000373941b\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65de401f6fea7b015bd1faaf\",\"title\":\"Directus\",\"slug\":\"directus\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"672e3bc619224a00034cde3d\"},{\"doc\":\"65de401f6fea7b015bd1faaf\",\"url\":\"/compare/directus\",\"label\":\"Directus\",\"id\":\"672e3bc619224a00034cde3e\"}]}},\"label\":\"Payload vs Directus\"},\"id\":\"672265f69d0bce000373941d\"}]},\"id\":\"672265ad9d0bce0003739419\"},{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"tag\":\"AGENCY TESTIMONIAL\",\"label\":{\"root\":{\"children\":[{\"children\":[{\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"\\\"Payload has transformed the way our clients manage content. It's an indispensable tool for any modern agency.\\\"\",\"type\":\"text\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"heading\",\"version\":1,\"tag\":\"h4\"}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}},\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65e62d33ce19cad022049171\",\"title\":\"Become a Payload Partner\",\"slug\":\"become-a-partner\",\"breadcrumbs\":[{\"doc\":\"65e62d33ce19cad022049171\",\"url\":\"/become-a-partner\",\"label\":\"Become a Payload Partner\",\"id\":\"66e9a8da67e341109bc09e35\"}]}},\"label\":\"Become a Partner\"},\"id\":\"6722692b181010000377a55a\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/partners\",\"label\":\"Find a Partner\"},\"id\":\"6722693a181010000377a55b\"}]},\"listLinks\":{\"links\":[]},\"id\":\"672266cde9f80e000998f529\"}],\"id\":\"65d4d9efe9fe56cf2b5f32fb\"},{\"label\":\"Developers\",\"enableDirectLink\":true,\"enableDropdown\":true,\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aabcf2fa327a417862bb7f\",\"title\":\"Developers\",\"slug\":\"developers\",\"breadcrumbs\":[{\"doc\":\"65aabcf2fa327a417862bb7f\",\"url\":\"/developers\",\"label\":\"Developers\",\"id\":\"6716cda483f57500031ea25f\"}]}}},\"description\":\"Code-based nature means you can build on top of it to power anything.\",\"descriptionLinks\":[],\"navItems\":[{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"Resources\",\"links\":[{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/docs\",\"label\":\"Documentation\"},\"id\":\"65e1be3486c23b0764901fb6\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload/tree/main/examples\",\"label\":\"Examples\"},\"id\":\"65e1be5d86c23b0764901fb7\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload/tree/main/templates\",\"label\":\"Templates\"},\"id\":\"65e1be6386c23b0764901fb8\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload\",\"label\":\"GitHub\"},\"id\":\"65e1be8186c23b0764901fb9\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload/releases\",\"label\":\"Releases\"},\"id\":\"673654b5b51317b8269142a8\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/blog\",\"label\":\"Blog\"},\"id\":\"666210b5fb64712933eebd96\"}]},\"id\":\"65e1be2186c23b0764901fb5\"},{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"Community\",\"links\":[{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://github.com/payloadcms/payload/discussions/categories/roadmap\",\"label\":\"Roadmap\"},\"id\":\"65e1bf4186c23b0764901fbb\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"reference\":null,\"url\":\"https://discord.com/invite/r6sCXqVk3v\",\"label\":\"Discord\"},\"id\":\"65e1bf5386c23b0764901fbc\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/community-help\",\"label\":\"Community Help\"},\"id\":\"65e1bf6186c23b0764901fbd\"}]},\"id\":\"65e1bedc86c23b0764901fba\"},{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"tag\":\"Payload Cloud\",\"label\":{\"root\":{\"type\":\"root\",\"children\":[{\"type\":\"heading\",\"children\":[{\"type\":\"text\",\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"Deploy your entire stack in one place with Payload Cloud.\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"tag\":\"h4\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"version\":1}},\"links\":[{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/login\",\"label\":\"Login\"},\"id\":\"65e1c02b86c23b0764901fc0\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6429f73faea011d1b4735131\",\"title\":\"Payload Cloud Pricing\",\"slug\":\"cloud-pricing\",\"breadcrumbs\":[{\"doc\":\"6429f73faea011d1b4735131\",\"url\":\"/cloud-pricing\",\"label\":\"Payload Cloud Pricing\",\"id\":\"6733b66228675e00035f99e1\"}]}},\"label\":\"Cloud Pricing\"},\"id\":\"65e1c03686c23b0764901fc1\"}]},\"listLinks\":{\"links\":[]},\"id\":\"65e1c00b86c23b0764901fbf\"}],\"id\":\"65e1be0386c23b0764901fb4\"},{\"label\":\"Enterprise\",\"enableDirectLink\":true,\"enableDropdown\":true,\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"64515c36006aeaef3d3d62c1\",\"title\":\"Payload for Enterprise\",\"slug\":\"enterprise\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8dd67e341109bc09e6d\"}]}},\"url\":\"/enterpr\"},\"description\":\"It’s time to take back your content infrastructure.\",\"descriptionLinks\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6362714c8500b86c17b16b78\",\"title\":\"Talk to us\",\"slug\":\"talk-to-us\",\"breadcrumbs\":[{\"doc\":\"6362714c8500b86c17b16b78\",\"url\":\"/talk-to-us\",\"label\":\"Talk to us\",\"id\":\"6740b4f18939b700031acbe9\"}]}},\"url\":\"https://demo.payloadcms.com/\",\"label\":\"Schedule a Demo\"},\"id\":\"65d51d0e79b580a77c5b6227\"}],\"navItems\":[{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"Enterprise Features\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6582f8742693ab4da8ed6601\",\"title\":\"Single Sign-On (SSO)\",\"slug\":\"single-sign-on-sso\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8de67e341109bc09e78\"},{\"doc\":\"6582f8742693ab4da8ed6601\",\"url\":\"/enterprise/single-sign-on-sso\",\"label\":\"Single Sign-On (SSO)\",\"id\":\"66e9a8de67e341109bc09e79\"}]}},\"label\":\"SSO\"},\"id\":\"65efaf38e920e8aba905be5e\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"67095b76a670b2b9de919cda\",\"title\":\"AI Auto-Embedding\",\"slug\":\"ai-framework\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"6740c888520e8e00034be568\"},{\"doc\":\"67095b76a670b2b9de919cda\",\"url\":\"/enterprise/ai-framework\",\"label\":\"AI Auto-Embedding\",\"id\":\"6740c888520e8e00034be569\"}]}},\"label\":\"AI Auto-Embedding\"},\"id\":\"673e91b8e9c9674bce3541a1\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6580ab1a2693ab4da8e670cf\",\"title\":\"Publishing Workflows\",\"slug\":\"publishing-workflows\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8de67e341109bc09e7a\"},{\"doc\":\"6580ab1a2693ab4da8e670cf\",\"url\":\"/enterprise/publishing-workflows\",\"label\":\"Publishing Workflows\",\"id\":\"66e9a8de67e341109bc09e7b\"}]}},\"label\":\"Publishing Workflows\"},\"id\":\"65efaf43e920e8aba905be5f\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6582fb422693ab4da8ed728c\",\"title\":\"Visual Editor\",\"slug\":\"visual-editor\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"673b8cc0f2827c000311de9c\"},{\"doc\":\"6582fb422693ab4da8ed728c\",\"url\":\"/enterprise/visual-editor\",\"label\":\"Visual Editor\",\"id\":\"673b8cc0f2827c000311de9d\"}]}},\"label\":\"Visual Editor\"},\"id\":\"65efaf53e920e8aba905be60\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"66674dbf5fdf174caf28af9a\",\"title\":\"A/B Testing\",\"slug\":\"headless-ab-variant-testing\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"66e9a8de67e341109bc09e7c\"},{\"doc\":\"66674dbf5fdf174caf28af9a\",\"url\":\"/enterprise/headless-ab-variant-testing\",\"label\":\"A/B Testing\",\"id\":\"66e9a8de67e341109bc09e7d\"}]}},\"label\":\"Static A/B testing\"},\"id\":\"65efaf7de920e8aba905be62\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6585ae1c50262a9de087c197\",\"title\":\"Enterprise AI\",\"slug\":\"enterprise-ai\",\"breadcrumbs\":[{\"doc\":\"64515c36006aeaef3d3d62c1\",\"url\":\"/enterprise\",\"label\":\"Payload for Enterprise\",\"id\":\"673b8e1838406000039bc278\"},{\"doc\":\"6585ae1c50262a9de087c197\",\"url\":\"/enterprise/enterprise-ai\",\"label\":\"Enterprise AI\",\"id\":\"673b8e1838406000039bc279\"}]}},\"label\":\"AI features\"},\"id\":\"65efaf69e920e8aba905be61\"}]},\"id\":\"65d51d3479b580a77c5b6229\"},{\"style\":\"list\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"links\":[]},\"listLinks\":{\"tag\":\"Customer Stories\",\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"65df959d5412b96c3774b300\",\"title\":\"Microsoft\",\"featuredImage\":\"65e20b6b9a2904b291d8d6b0\",\"slug\":\"microsoft\",\"url\":\"https://create.microsoft.com/en-us/learn\"}},\"url\":\"\",\"label\":\"Microsoft\"},\"id\":\"65d51dac79b580a77c5b6231\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"663e5ff317e86af24a14667f\",\"title\":\"Blue Origin\",\"featuredImage\":\"664cdff8f15c266ea3f35c62\",\"slug\":\"blue-origin-club-for-the-future\",\"url\":\"https://www.clubforfuture.org/postcards\"}},\"label\":\"Blue Origin\"},\"id\":\"6656363f8f0ef0f7d812334a\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"65df85de5412b96c3774a72b\",\"title\":\"Hello Bello\",\"featuredImage\":\"65df923b5412b96c3774b1f5\",\"slug\":\"hello-bello\",\"url\":\"https://hellobello.com\"}},\"label\":\"Hello Bello\"},\"id\":\"65d51dbb79b580a77c5b6232\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"66198b47d5974b13de0837e5\",\"title\":\"Mythical Society\",\"featuredImage\":\"66282250755bf549f20ac73c\",\"slug\":\"mythical-society\",\"url\":\"https://www.mythicalsociety.com/\"}},\"label\":\"Mythical Society\"},\"id\":\"65d51de079b580a77c5b6235\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"65df9dd27f8635bf2efc401f\",\"title\":\"Tekton\",\"featuredImage\":\"65de401b6fea7b015bd1fa80\",\"slug\":\"tekton\",\"url\":\"https://tekton.com\"}},\"label\":\"Tekton\"},\"id\":\"65e22884df88b478a1e6db58\"}]},\"id\":\"65d51d9d79b580a77c5b6230\"},{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"reference\",\"reference\":null}},\"featuredLink\":{\"tag\":\"Featured Customer Story\",\"label\":{\"root\":{\"type\":\"root\",\"children\":[{\"type\":\"heading\",\"children\":[{\"type\":\"text\",\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"Microsoft chose Payload to tell the world about AI.\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"tag\":\"h4\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"version\":1}},\"links\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"case-studies\",\"value\":{\"id\":\"65df959d5412b96c3774b300\",\"title\":\"Microsoft\",\"featuredImage\":\"65e20b6b9a2904b291d8d6b0\",\"slug\":\"microsoft\",\"url\":\"https://create.microsoft.com/en-us/learn\"}},\"label\":\"Read the case study\"},\"id\":\"65d51e0c79b580a77c5b6237\"}]},\"listLinks\":{\"links\":[]},\"id\":\"65d51df579b580a77c5b6236\"}],\"id\":\"65d51ce379b580a77c5b6226\"},{\"label\":\"Docs\",\"enableDirectLink\":true,\"link\":{\"type\":\"custom\",\"newTab\":false,\"reference\":null,\"url\":\"/docs\"},\"descriptionLinks\":[],\"navItems\":[{\"style\":\"featured\",\"defaultLink\":{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"https://github.com/payloadcms/payload\",\"label\":\"GitHub\"}},\"featuredLink\":{\"links\":[{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"https://github.com/payloadcms/payload\",\"label\":\"GitHub\"},\"id\":\"65d55a9dfa96b45f5e85af05\"}]},\"listLinks\":{\"links\":[]},\"id\":\"65d559a4a4b1c51621f5bbdb\"}],\"id\":\"65d5599ca4b1c51621f5bbda\"}],\"menuCta\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/get-started\",\"label\":\"Get Started\"},\"id\":\"6356ebbc6ad5ce88fb581531\"}],[\"$\",\"div\",null,{\"children\":[[\"$\",\"$L31\",null,{}],[\"$\",\"div\",null,{\"id\":\"docsearch\"}],[\"$\",\"$L30\",null,{\"columns\":[{\"label\":\"Use Cases\",\"navItems\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65aecafefa327a41786d6431\",\"title\":\"Headless CMS\",\"slug\":\"headless-cms\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dc67e341109bc09e55\"},{\"doc\":\"65aecafefa327a41786d6431\",\"url\":\"/use-cases/headless-cms\",\"label\":\"Headless CMS\",\"id\":\"66e9a8dc67e341109bc09e56\"}]}},\"label\":\"Content Management System\"},\"id\":\"65e09c6139c8d261526eba36\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65d8c8e5fa0a8be86415cde0\",\"title\":\"Enterprise App Builder \",\"slug\":\"enterprise-app-builder\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8db67e341109bc09e4f\"},{\"doc\":\"65d8c8e5fa0a8be86415cde0\",\"url\":\"/use-cases/enterprise-app-builder\",\"label\":\"Enterprise App Builder \",\"id\":\"66e9a8db67e341109bc09e50\"}]}},\"label\":\"Enterprise App Builder\"},\"id\":\"65e09c7139c8d261526eba37\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6515c182679a6536704e5988\",\"title\":\"Headless E-commerce\",\"slug\":\"headless-ecommerce\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"66e9a8dd67e341109bc09e67\"},{\"doc\":\"6515c182679a6536704e5988\",\"url\":\"/use-cases/headless-ecommerce\",\"label\":\"Headless E-commerce\",\"id\":\"66e9a8dd67e341109bc09e68\"}]}},\"label\":\"Headless E-Commerce\"},\"id\":\"65e09c7e39c8d261526eba38\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65d78062fa286280ee430772\",\"title\":\"Digital Asset Management\",\"slug\":\"digital-asset-management\",\"breadcrumbs\":[{\"doc\":\"65dd43d561b3c803918e03d4\",\"url\":\"/use-cases\",\"label\":\"Use Cases\",\"id\":\"673b8d00f2827c000311de9e\"},{\"doc\":\"65d78062fa286280ee430772\",\"url\":\"/use-cases/digital-asset-management\",\"label\":\"Digital Asset Management\",\"id\":\"673b8d00f2827c000311de9f\"}]}},\"label\":\"Digital Asset Management\"},\"id\":\"65e09c8c39c8d261526eba39\"}],\"id\":\"65e09c5f39c8d261526eba35\"},{\"label\":\"Developers\",\"navItems\":[{\"link\":{\"type\":\"custom\",\"reference\":{\"relationTo\":\"pages\",\"value\":\"636c1a8b74e94056f1fada16\"},\"url\":\"https://payloadcms.com/new\",\"label\":\"Payload Cloud\"},\"id\":\"65e09c9f39c8d261526eba3b\"},{\"link\":{\"type\":\"custom\",\"url\":\"/docs/getting-started/what-is-payload\",\"label\":\"Documentation\",\"reference\":null},\"id\":\"65e09cb639c8d261526eba3c\"},{\"link\":{\"type\":\"custom\",\"url\":\"/community-help\",\"label\":\"Community Help\",\"reference\":null},\"id\":\"65e09cd839c8d261526eba3d\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"url\":\"https://github.com/payloadcms/payload/discussions/categories/roadmap?discussions_q=category%3ARoadmap+\",\"label\":\"Roadmap\",\"reference\":null},\"id\":\"65e09cea39c8d261526eba3e\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"url\":\"https://github.com/payloadcms/payload/tree/main/templates\",\"label\":\"Templates\",\"reference\":null},\"id\":\"65e09d3039c8d261526eba3f\"},{\"link\":{\"type\":\"custom\",\"newTab\":true,\"url\":\"https://github.com/payloadcms/payload/releases\",\"label\":\"Releases\",\"reference\":null},\"id\":\"6736575db51317b8269142a9\"}],\"id\":\"65e09c9939c8d261526eba3a\"},{\"label\":\"Company\",\"navItems\":[{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6429f73faea011d1b4735131\",\"title\":\"Payload Cloud Pricing\",\"slug\":\"cloud-pricing\",\"breadcrumbs\":[{\"doc\":\"6429f73faea011d1b4735131\",\"url\":\"/cloud-pricing\",\"label\":\"Payload Cloud Pricing\",\"id\":\"6733b66228675e00035f99e1\"}]}},\"label\":\"Pricing\"},\"id\":\"65e09d5e39c8d261526eba42\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6362714c8500b86c17b16b78\",\"title\":\"Talk to us\",\"slug\":\"talk-to-us\",\"breadcrumbs\":[{\"doc\":\"6362714c8500b86c17b16b78\",\"url\":\"/talk-to-us\",\"label\":\"Talk to us\",\"id\":\"6740b4f18939b700031acbe9\"}]}},\"label\":\"Enterprise\"},\"id\":\"65e09d7239c8d261526eba43\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"6363f57fe2d7a415f906bf57\",\"title\":\"Case Studies\",\"slug\":\"case-studies\",\"breadcrumbs\":[{\"doc\":\"6363f57fe2d7a415f906bf57\",\"url\":\"/case-studies\",\"label\":\"Case Studies\",\"id\":\"6734ca4469993200036409d7\"}]}},\"label\":\"Case Studies\"},\"id\":\"65e09d9239c8d261526eba46\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65e62d33ce19cad022049171\",\"title\":\"Become a Payload Partner\",\"slug\":\"become-a-partner\",\"breadcrumbs\":[{\"doc\":\"65e62d33ce19cad022049171\",\"url\":\"/become-a-partner\",\"label\":\"Become a Payload Partner\",\"id\":\"66e9a8da67e341109bc09e35\"}]}},\"label\":\"Partner With Us\"},\"id\":\"65e09d7e39c8d261526eba44\"},{\"link\":{\"type\":\"custom\",\"url\":\"/partners\",\"label\":\"Find a Partner\",\"reference\":null},\"id\":\"66d0912d92e684b1f3e1bed9\"},{\"link\":{\"type\":\"custom\",\"url\":\"/blog\",\"label\":\"Blog\",\"reference\":null},\"id\":\"65e09d9b39c8d261526eba47\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"65b1beb3fa327a417876b63d\",\"title\":\"Security\",\"slug\":\"security\",\"breadcrumbs\":[{\"doc\":\"65b1beb3fa327a417876b63d\",\"url\":\"/security\",\"label\":\"Security\",\"id\":\"66e9a8dc67e341109bc09e53\"}]}},\"label\":\"Security\"},\"id\":\"65e76c53590a277ae5ad8cbb\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636d5408969bcf4617bd7971\",\"title\":\"Compare\",\"slug\":\"compare\",\"breadcrumbs\":[{\"doc\":\"636d5408969bcf4617bd7971\",\"url\":\"/compare\",\"label\":\"Compare\",\"id\":\"66e9a8df67e341109bc09e8d\"}]}},\"label\":\"Compare Payload\"},\"id\":\"65e09d8939c8d261526eba45\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636d859b0e6e763e9a571056\",\"title\":\"Terms\",\"slug\":\"terms\",\"breadcrumbs\":[{\"doc\":\"636d859b0e6e763e9a571056\",\"url\":\"/terms\",\"label\":\"Terms\",\"id\":\"66e9a8de67e341109bc09e8b\"}]}},\"url\":\"\",\"label\":\"Terms of Service\"},\"id\":\"65e09da839c8d261526eba48\"},{\"link\":{\"type\":\"custom\",\"reference\":null,\"url\":\"/privacy\",\"label\":\"Privacy Policy\"},\"id\":\"65e09db439c8d261526eba49\"},{\"link\":{\"type\":\"reference\",\"reference\":{\"relationTo\":\"pages\",\"value\":{\"id\":\"636d818f0e6e763e9a56f09d\",\"title\":\"Contact\",\"slug\":\"contact\",\"breadcrumbs\":[{\"doc\":\"636d818f0e6e763e9a56f09d\",\"url\":\"/contact\",\"label\":\"Contact\",\"id\":\"66e9a8de67e341109bc09e8c\"}]}},\"label\":\"Contact\"},\"id\":\"65e8e2887a897c4ca1a59be5\"}],\"id\":\"65e09d5839c8d261526eba41\"}],\"globalType\":\"footer\",\"createdAt\":\"2022-11-04T18:46:31.302Z\",\"updatedAt\":\"2024-11-14T20:02:58.886Z\",\"id\":\"63655e0793374a39945c71e5\"}]]}]]\n"])</script><script>self.__next_f.push([1,"26:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"24:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"1\",{\"children\":\"Localization | Documentation | Payload\"}],[\"$\",\"meta\",\"2\",{\"name\":\"description\",\"content\":\"Add and maintain as many locales as you need by adding Localization to your Payload Config, set options for default locale, fallbacks, fields and more.\"}],[\"$\",\"meta\",\"3\",{\"property\":\"og:title\",\"content\":\"Localization | Documentation | Payload\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:description\",\"content\":\"Payload is a headless CMS and application framework built with TypeScript, Node.js, React and MongoDB\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:url\",\"content\":\"https://payloadcms.com/docs/configuration/localization\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:site_name\",\"content\":\"Payload\"}],[\"$\",\"meta\",\"7\",{\"property\":\"og:image\",\"content\":\"https://payloadcms.com/api/og?topic=configuration\u0026title=Localization\"}],[\"$\",\"meta\",\"8\",{\"property\":\"og:type\",\"content\":\"website\"}],[\"$\",\"meta\",\"9\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"10\",{\"name\":\"twitter:creator\",\"content\":\"@payloadcms\"}],[\"$\",\"meta\",\"11\",{\"name\":\"twitter:title\",\"content\":\"Localization | Documentation | Payload\"}],[\"$\",\"meta\",\"12\",{\"name\":\"twitter:description\",\"content\":\"Payload is a headless CMS and application framework built with TypeScript, Node.js, React and MongoDB\"}],[\"$\",\"meta\",\"13\",{\"name\":\"twitter:image\",\"content\":\"https://payloadcms.com/api/og?topic=configuration\u0026title=Localization\"}],[\"$\",\"link\",\"14\",{\"rel\":\"apple-touch-icon\",\"href\":\"/apple-icon.png?1be71cc371876318\",\"type\":\"image/png\",\"sizes\":\"180x180\"}]]\n"])</script><script>self.__next_f.push([1,"22:null\n"])</script><script>self.__next_f.push([1,"32:I[78681,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"JumplistProvider\"]\n33:I[89711,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"DocsNavigation\"]\na8:\"$Sreact.suspense\"\naa:I[26658,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746"])</script><script>self.__next_f.push([1,"769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"\"]\nae:I[8841,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"VersionSelector\"]\naf:I[23583,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/c"])</script><script>self.__next_f.push([1,"hunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"TableOfContents\"]\nb0:I[68271,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"GithubStarsPill\"]\nb1:I[54149,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"DiscordUsersPill\"]\n34:T238f,"])</script><script>self.__next_f.push([1,"\n\u003cYouTube\n id=\"In_lFhzmbME\"\n title=\"Payload Introduction - Closing the Gap Between Headless CMS and Application Frameworks\"\n/\u003e\n\n**Payload is the Next.js fullstack framework.** Write a Payload Config and instantly get:\n\n- A full Admin Panel using React server / client components, matching the shape of your data and completely extensible with your own React components\n- Automatic database schema, including direct DB access and ownership, with migrations, transactions, proper indexing, and more\n- Instant REST, GraphQL, and straight-to-DB Node.js APIs\n- Authentication which can be used in your own apps\n- A deeply customizable access control pattern\n- File storage and image management tools like cropping / focal point selection\n- Live preview - see your frontend render content changes in realtime as you update\n- Lots more\n\n### Instant backend superpowers\n\nNo matter what you're building, Payload will give you backend superpowers. Your entire Payload config can be installed in one line into any existing Next.js app, and is designed to catapult your development process. Payload takes the most complex and time-consuming parts of any modern web app and makes them simple.\n\n### Open source - deploy anywhere, including Vercel\n\nIt's fully open source with an MIT license and you can self-host anywhere that you can run a Node.js app. You can also deploy serverless to hosts like Vercel, right inside your existing Next.js application.\n\n### Code-first and version controlled\n\nIn Payload, there are no \"click ops\" - as in clicking around in an Admin Panel to define your schema. In Payload, everything is done the right way—code-first and version controlled like a proper backend. But once developers define how Payload should work, non-technical users can independently make use of its Admin Panel to manage whatever they need to without having to know code whatsoever.\n\n### Fully extensible\n\nEven in spite of how much you get out of the box, you still have full control over every aspect of your app - be it database, admin UI, or anything else. Every part of Payload has been designed to be extensible and customizable with modern TypeScript / React. And you'll fully understand the code that you write.\n\n## Use Cases\n\nPayload started as a headless Content Management System (CMS), but since, we've seen our community leverage Payload in ways far outside of simply managing pages and blog posts. It's grown into a full-stack TypeScript app framework.\n\nLarge enterprises use Payload to power significant internal tools, retailers power their entire storefronts without the need for headless Shopify, and massive amounts of digital assets are stored + managed within Payload. Of course, websites large and small still use Payload for content management as well.\n\n### Headless CMS\n\nThe biggest barrier in large web projects cited by marketers is engineering. On the flip side, engineers say the opposite. This is a big problem that has yet to be solved even though we have countless CMS options.\n\nPayload has restored a little love back into the dev / marketer equation with features like Live Preview, redirects, form builders, visual editing, static A/B testing, and more. But even with all this focus on marketing efficiency, we aren't compromising on the developer experience. That way engineers and marketers alike can be proud of the products they build.\n\nIf you're building a website and your frontend is on Next.js, then Payload is a no-brainer.\n\n\u003cBanner type=\"success\"\u003e\n Instead of going out and signing up for a SaaS vendor that makes it so you have to manage two completely separate concerns, with little to no native connection back and forth, just install Payload in your existing Next.js repo and instantly get a full CMS.\n\u003c/Banner\u003e\n\nGet started with Payload as a CMS using our official Website template:\n\n```\nnpx create-payload-app@latest -t website\n```\n\n### Enterprise Tool\n\nWhen a large organization starts up a new software initiative, there's a lot of plumbing to take care of.\n\n- Scaffold the data layer with an ORM or an app framework like Ruby on Rails or Laravel\n- Implement their SSO provider for authentication\n- Design an access control pattern for authorization\n- Open up any REST endpoints required or implement GraphQL queries / mutations\n- Implement a migrations workflow for the database as it changes over time\n- Integrate with other third party solutions by crafting a system of webhooks or similar\n\nAnd then there's the [Admin Panel](../admin/overview). Most enterprise tools require an admin UI, and building one from scratch can be the most time-consuming aspect of any new enterprise tool. There are off-the-shelf packages for app frameworks like Rails, but often the customization is so involved that using Material UI or similar from scratch might be better.\n\nThen there are no-code admin builders that could be used. However, wiring up access control and the connection to the data layer, with proper version control, makes this a challenging task as well.\n\nThat's where Payload comes in. Payload instantly provides all of this out of the box, making complex internal tools extremely simple to both spin up and maintain over time. The only custom code that will need to be written is any custom business logic. That means Payload can expedite timelines, keep budgets low, and allow engineers to focus on their specific requirements rather than complex backend / admin UI plumbing.\n\nGenerally, the best place to start for a new enterprise tool is with a blank canvas, where you can define your own functionality:\n\n```\nnpx create-payload-app@latest -t blank\n```\n\n### Headless Commerce\n\nCompanies who prioritize UX generally run into frontend constraints with traditional commerce vendors. These companies will then opt for frontend frameworks like Next.js which allow them to fine-tune their user experience as much as possible—promoting conversions, personalizing experiences, and optimizing for SEO.\n\nBut the challenge with using something like Next.js for headless commerce is that in order for non-technical users to manage the storefront, you instantly need to pair a headless commerce product with a headless CMS. Then, your editors need to bounce back and forth between different admin UIs for different functionality. The code required to seamlessly glue them together on the frontend becomes overly complex.\n\nPayload can integrate with any payment processor like Stripe and its content authoring capabilities allow it to manage every aspect of a storefront—all in one place.\n\nIf you can build your storefront with a single backend, and only offload things like payment processing, the code will be simpler and the editing experience will be significantly streamlined. Manage products, catalogs, page content, media, and more—all in one spot.\n\n### Digital Asset Management\n\nPayload's API-first tagging, sorting, and querying engine lends itself perfectly to all types of content that a CMS might ordinarily store, but these strong fundamentals also make it a formidable Digital Asset Management (DAM) tool as well.\n\nSimilarly to the Ecommerce use case above, if an organization uses a CMS for its content but a separate DAM for its digital assets, administrators of both tools will need to juggle completely different services for tasks that are closely related. Two subscriptions will need to be managed, two sets of infrastructure will need to be provisioned, and two admin UIs need to be used / learned.\n\nPayload flattens CMS and DAM into a single tool that makes no compromises on either side. Powerful features like folder-based organization, file versioning, bulk upload, and media access control allow Payload to simultaneously function as a full Digital Asset Management platform as well as a Content Management System at the same time.\n\n[Click here](https://payloadcms.com/use-cases/digital-asset-management) for more information on how to get started with Payload as a DAM.\n\n## Choosing a Framework\n\nPayload is a great choice for applications of all sizes and types, but it might not be the right choice for every project. Here are some guidelines to help you decide if Payload is the right choice for your project.\n\n### When Payload might be for you\n\n- If data ownership and privacy are important to you, and you don't want to allow another proprietary SaaS vendor to host and own your data\n- If you're building a Next.js site that needs a CMS\n- If you need to re-use your data outside of a SaaS API\n- If what you're building has custom business logic requirements outside of a typical headless CMS\n- You want to deploy serverless on platforms like Vercel\n\n### When Payload might not be for you\n\n- If you can manage your project fully with code, and don't need an admin UI\n- If you are building a website that fits within the limits a tool like Webflow or Framer\n- If you already have a full database and just need to visualize the data somehow\n- If you are confident that you won't need code / data ownership at any point in the future\n\nReady to get started? First, let's review some high-level concepts that are used in Payload.\n"])</script><script>self.__next_f.push([1,"35:T2346,"])</script><script>self.__next_f.push([1,"\nPayload is based around a small and intuitive set of high-level concepts. Before starting to work with Payload, it's a good idea to familiarize yourself with these concepts in order to establish a common language and understanding when discussing Payload.\n\n## Config\n\nThe Payload Config is central to everything that Payload does. It allows for the deep configuration of your application through a simple and intuitive API. The Payload Config is a fully-typed JavaScript object that can be infinitely extended upon. [More details](../configuration/overview).\n\n## Database\n\nPayload is database agnostic, meaning you can use any type of database behind Payload's familiar APIs through what is known as a Database Adapter. [More details](../database/overview).\n\n## Collections\n\nA Collection is a group of records, called Documents, that all share a common schema. Each Collection is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define. [More details](../configuration/collections).\n\n## Globals\n\nGlobals are in many ways similar to [Collections](../configuration/collections), except they correspond to only a single Document. Each Global is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define. [More details](../configuration/globals).\n\n## Fields\n\nFields are the building blocks of Payload. They define the schema of the Documents that will be stored in the [Database](../database/overview), as well as automatically generate the corresponding UI within the Admin Panel. [More details](../fields/overview).\n\n## Hooks\n\nHooks allow you to execute your own side effects during specific events of the Document lifecycle, such as before read, after create, etc. [More details](../hooks/overview).\n\n## Authentication\n\nPayload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the Admin Panel, as well as your own external applications. [More details](../authentication/overview).\n\n## Access Control\n\nAccess Control determines what a user can and cannot do with any given Document, such as read, update, etc., as well as what they can and cannot see within the Admin Panel. [More details](../access-control/overview).\n\n## Admin Panel\n\nPayload dynamically generates a beautiful, fully type-safe interface to manage your users and data. The Admin Panel is a React application built using the Next.js App Router. [More details](../admin/overview).\n\n## Retrieving Data\n\nEverything Payload does (create, read, update, delete, login, logout, etc.) is exposed to you via three APIs:\n\n- [Local API](#local-api) - Extremely fast, direct-to-database access\n- [REST API](#rest-api) - Standard HTTP endpoints for querying and mutating data\n- [GraphQL](#graphql) - A full GraphQL API with a GraphQL Playground\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n All of these APIs share the exact same query language. [More details](../queries/overview).\n\u003c/Banner\u003e\n\n### Local API\n\nBy far one of the most powerful aspects of Payload is the fact that it gives you direct-to-database access to your data through the [Local API](../local-api/overview). It's _extremely_ fast and does not incur any typical HTTP overhead—you query your database directly in Node.js.\n\nThe Local API is written in TypeScript, and so it is strongly typed and extremely nice to use. It works anywhere on the server, including custom Next.js Routes, Payload Hooks, Payload Access Control, and React Server Components.\n\nHere's a quick example of a React Server Component fetching data using the Local API:\n\n```tsx\nimport React from 'react'\nimport config from '@payload-config'\nimport { getPayload } from 'payload'\n\nconst MyServerComponent: React.FC = () =\u003e {\n const payload = await getPayload({ config })\n\n // The `findResult` here will be fully typed as `PaginatedDocs\u003cPage\u003e`,\n // where you will have the `docs` that are returned as well as\n // information about how many items are returned / are available in total / etc\n const findResult = await payload.find({ collection: 'pages' })\n\n return (\n \u003cul\u003e\n {findResult.docs.map((page) =\u003e {\n // Render whatever here!\n // The `page` is fully typed as your Pages collection!\n })}\n \u003c/ul\u003e\n )\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n For more information about the Local API, [click here](../local-api/overview).\n\u003c/Banner\u003e\n\n### REST API\n\nBy default, the Payload [REST API](../rest-api/overview) is mounted automatically for you at the `/api` path of your app.\n\nFor example, if you have a Collection called `pages`:\n\n```ts\nfetch('https://localhost:3000/api/pages') // highlight-line\n .then((res) =\u003e res.json())\n .then((data) =\u003e console.log(data))\n```\n\n\u003cBanner type=\"info\"\u003e\n For more information about the REST API, [click here](../rest-api/overview).\n\u003c/Banner\u003e\n\n### GraphQL API\n\nPayload automatically exposes GraphQL queries and mutations through a dedicated [GraphQL API](../graphql/overview). By default, the GraphQL route handler is mounted at the `/api/graphql` path of your app. You'll also find a full GraphQL Playground which can be accessible at the `/api/graphql-playground` path of your app.\n\nYou can use any GraphQL client with Payload's GraphQL endpoint. Here are a few packages:\n\n- [`graphql-request`](https://www.npmjs.com/package/graphql-request) - a very lightweight GraphQL client\n- [`@apollo/client`](https://www.apollographql.com/docs/react/api/core/ApolloClient/) - an industry-standard GraphQL client with lots of nice features\n\n\u003cBanner type=\"info\"\u003e\n For more information about the GraphQL API, [click here](../graphql/overview).\n\u003c/Banner\u003e\n\n## Package Structure\n\nPayload is abstracted into a set of dedicated packages to keep the core `payload` package as lightweight as possible. This allows you to only install the parts of Payload based on your unique project requirements.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n Version numbers of all official Payload packages are always published in sync. You should make sure that you always use matching versions for all official Payload packages.\n\u003c/Banner\u003e\n\n`payload`\n\nThe `payload` package is where core business logic for Payload lives. You can think of Payload as an ORM with superpowers—it contains the logic for all Payload \"operations\" like `find`, `create`, `update`, and `delete` and exposes a [Local API](../local-api/overview). It executes [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Validation](../fields/overview#validation), and more.\n\nPayload itself is extremely compact, and can be used in any Node environment. As long as you have `payload` installed and you have access to your Payload Config, you can query and mutate your database directly without going through an unnecessary HTTP layer.\n\nPayload also contains all TypeScript definitions, which can be imported from `payload` directly.\n\nHere's how to import some common Payload types:\n\n```ts\nimport { Config, CollectionConfig, GlobalConfig, Field } from 'payload'\n```\n\n`@payloadcms/next`\n\nWhereas Payload itself is responsible for direct database access, and control over Payload business logic, the `@payloadcms/next` package is responsible for the Admin Panel and the entire HTTP layer that Payload exposes, including the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview).\n\n`@payloadcms/graphql`\n\nAll of Payload's GraphQL functionality is abstracted into a separate package. Payload, its Admin UI, and REST API have absolutely no overlap with GraphQL, and you will incur no performance overhead from GraphQL if you are not using it. However, it's installed within the `@payloadcms/next` package so you don't have to install it manually. You do, however, need to have GraphQL installed separately in your `package.json` if you are using GraphQL.\n\n`@payloadcms/ui`\n\nThis is the UI library that Payload's Admin Panel uses. All components are exported from this package and can be re-used as you build extensions to the Payload admin UI, or want to use Payload components in your own React apps. Some exports are server components and some are client components.\n\n`@payloadcms/db-postgres`, `@payloadcms/db-vercel-postgres`, `@payloadcms/db-mongodb`\n\nYou can choose which Database Adapter you'd like to use for your project, and no matter which you choose, the entire data layer for Payload is contained within these packages. You can only use one at a time for any given project.\n\n`@payloadcms/richtext-lexical`, `@payloadcms/richtext-slate`\n\nPayload's Rich Text functionality is abstracted into separate packages and if you want to enable Rich Text in your project, you'll need to install one of these packages. We recommend Lexical for all new projects, and this is where Payload will focus its efforts on from this point, but Slate is still supported if you have already built with it.\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n Rich Text is entirely optional and you may not need it for your project.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"36:T1c9f,"])</script><script>self.__next_f.push([1,"\n## Software Requirements\n\nPayload requires the following software:\n\n- Any JavaScript package manager (Yarn, NPM, or pnpm - pnpm is preferred)\n- Node.js version 20.9.0+\n- Any [compatible database](../database/overview) (MongoDB or Postgres)\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n Before proceeding any further, please ensure that you have the above requirements met.\n\u003c/Banner\u003e\n\n## Quickstart with create-payload-app\n\nTo quickly scaffold a new Payload app in the fastest way possible, you can use [create-payload-app](https://npmjs.com/package/create-payload-app). To do so, run the following command:\n\n```\nnpx create-payload-app\n```\n\nThen just follow the prompts! You'll get set up with a new folder and a functioning Payload app inside. You can then start [configuring your application](../configuration/overview).\n\n## Adding to an existing app\n\nAdding Payload to an existing Next.js app is super straightforward. You can either run the `npx create-payload-app` command inside your Next.js project's folder, or manually install Payload by following the steps below.\n\nIf you don't have a Next.js app already, but you still want to start a project from a blank Next.js app, you can create a new Next.js app using `npx create-next-app` - and then just follow the steps below to install Payload.\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e Next.js version 15 or higher is required for Payload.\n\u003c/Banner\u003e\n\n#### 1. Install the relevant packages\n\nFirst, you'll want to add the required Payload packages to your project and can do so by running the command below:\n\n```bash\npnpm i payload @payloadcms/next @payloadcms/richtext-lexical sharp graphql\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n Swap out `pnpm` for your package manager. If you are using NPM, you might need to install using legacy peer deps: `npm i --legacy-peer-deps`.\n\u003c/Banner\u003e\n\nNext, install a [Database Adapter](../database/overview). Payload requires a Database Adapter to establish a database connection. Payload works with all types of databases, but the most common are MongoDB and Postgres.\n\nTo install a Database Adapter, you can run **one** of the following commands:\n\n- To install the [MongoDB Adapter](../database/mongodb), run:\n ```bash\n pnpm i @payloadcms/db-mongodb\n ```\n\n- To install the [Postgres Adapter](../database/postgres), run:\n ```bash\n pnpm i @payloadcms/db-postgres\n ```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n New [Database Adapters](../database/overview) are becoming available every day. Check the docs for the most up-to-date list of what's available.\n\u003c/Banner\u003e\n\n#### 2. Copy Payload files into your Next.js app folder\n\nPayload installs directly in your Next.js `/app` folder, and you'll need to place some files into that folder for Payload to run. You can copy these files from the [Blank Template](https://github.com/payloadcms/payload/tree/main/templates/blank/src/app/(payload)) on GitHub. Once you have the required Payload files in place in your `/app` folder, you should have something like this:\n\n```plaintext\napp/\n├─ (payload)/\n├── // Payload files\n├─ (my-app)/\n├── // Your app files\n```\n\n_For an exact reference of the `(payload)` directory, see [Project Structure](../admin/overview#project-structure)._\n\n\u003cBanner type=\"warning\"\u003e\n You may need to copy all of your existing frontend files, including your existing root layout, into its own newly created [Route Group](https://nextjs.org/docs/app/building-your-application/routing/route-groups), i.e. `(my-app)`.\n\u003c/Banner\u003e\n\nThe files that Payload needs to have in your `/app` folder do not regenerate, and will never change. Once you slot them in, you never have to revisit them. They are not meant to be edited and simply import Payload dependencies from `@payloadcms/next` for the REST / GraphQL API and Admin Panel.\n\nYou can name the `(my-app)` folder anything you want. The name does not matter and will just be used to clarify your directory structure for yourself. Common names might be `(frontend)`, `(app)`, or similar. [More details](../admin/overview).\n\n#### 3. Add the Payload Plugin to your Next.js config\n\nPayload has a Next.js plugin that it uses to ensure compatibility with some of the packages Payload relies on, like `mongodb` or `drizzle-kit`.\n\nTo add the Payload Plugin, use `withPayload` in your `next.config.js`:\n\n```js\nimport { withPayload } from '@payloadcms/next/withPayload'\n\n/** @type {import('next').NextConfig} */\nconst nextConfig = {\n // Your Next.js config here\n experimental: {\n reactCompiler: false\n }\n}\n\n// Make sure you wrap your `nextConfig`\n// with the `withPayload` plugin\nexport default withPayload(nextConfig) // highlight-line\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n Payload is a fully ESM project, and that means the `withPayload` function is an ECMAScript module.\n\u003c/Banner\u003e\n\nTo import the Payload Plugin, you need to make sure your `next.config` file is set up to use ESM.\n\nYou can do this in one of two ways:\n\n1. Set your own project to use ESM, by adding `\"type\": \"module\"` to your `package.json` file\n2. Give your Next.js config the `.mjs` file extension\n\nIn either case, all `require`s and `export`s in your `next.config` file will need to be converted to `import` / `export` if they are not set up that way already.\n\n#### 4. Create a Payload Config and add it to your TypeScript config\n\nFinally, you need to create a [Payload Config](../configuration/overview). Generally the Payload Config is located at the root of your repository, or next to your `/app` folder, and is named `payload.config.ts`.\n\nHere's what Payload needs at a bare minimum:\n\n```ts\nimport sharp from 'sharp'\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\nimport { mongooseAdapter } from '@payloadcms/db-mongodb'\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // If you'd like to use Rich Text, pass your editor here\n editor: lexicalEditor(),\n\n // Define and configure your collections in this array\n collections: [],\n\n // Your Payload secret - should be a complex and secure string, unguessable\n secret: process.env.PAYLOAD_SECRET || '',\n // Whichever Database Adapter you're using should go here\n // Mongoose is shown as an example, but you can also use Postgres\n db: mongooseAdapter({\n url: process.env.DATABASE_URI || '',\n }),\n // If you want to resize images, crop, set focal point, etc.\n // make sure to install it and pass it to the config.\n // This is optional - if you don't need to do these things,\n // you don't need it!\n sharp,\n})\n```\n\nAlthough this is just the bare minimum config, there are _many_ more options that you can control here. To reference the full config and all of its options, [click here](../configuration/overview).\n\nOnce you have a Payload Config, update your `tsconfig` to include a `path` that points to it:\n\n```json\n{\n \"compilerOptions\": {\n \"paths\": {\n \"@payload-config\": [\n \"./payload.config.ts\"\n ]\n }\n },\n}\n```\n\n#### 5. Fire it up!\n\nAfter you've reached this point, it's time to boot up Payload. Start your project in your application's folder to get going. By default, the Next.js dev script is `pnpm dev` (or `npm run dev` if using NPM).\n\nAfter it starts, you can go to `http://localhost:3000/admin` to create your first Payload user!\n"])</script><script>self.__next_f.push([1,"37:T47af,"])</script><script>self.__next_f.push([1,"\nPayload is a _config-based_, code-first CMS and application framework. The Payload Config is central to everything that Payload does, allowing for deep configuration of your application through a simple and intuitive API. The Payload Config is a fully-typed JavaScript object that can be infinitely extended upon.\n\nEverything from your [Database](../database/overview) choice to the appearance of the [Admin Panel](../admin/overview) is fully controlled through the Payload Config. From here you can define [Fields](../fields/overview), add [Localization](./localization), enable [Authentication](../authentication/overview), configure [Access Control](../access-control/overview), and so much more.\n\nThe Payload Config is a `payload.config.ts` file typically located in the root of your project:\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // Your config goes here\n})\n```\n\nThe Payload Config is strongly typed and ties directly into Payload's TypeScript codebase. This means your IDE (such as VSCode) will provide helpful information like type-ahead suggestions while you write your config.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n The location of your Payload Config can be customized. [More details](#customizing--automating-config-location-detection).\n\u003c/Banner\u003e\n\n## Config Options\n\nTo author your Payload Config, first determine which [Database](../database/overview) you'd like to use, then use [Collections](./collections) or [Globals](./globals) to define the schema of your data through [Fields](../fields/overview).\n\nHere is one of the simplest possible Payload configs:\n\n```ts\nimport { buildConfig } from 'payload'\nimport { mongooseAdapter } from '@payloadcms/db-mongodb'\n\nexport default buildConfig({\n secret: process.env.PAYLOAD_SECRET,\n db: mongooseAdapter({\n url: process.env.DATABASE_URI,\n }),\n collections: [\n {\n slug: 'pages',\n fields: [\n {\n name: 'title',\n type: 'text'\n }\n ]\n }\n ],\n})\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Option | Description |\n|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`admin`** | The configuration options for the Admin Panel, including Custom Components, Live Preview, etc. [More details](../admin/overview#admin-options). |\n| **`bin`** | Register custom bin scripts for Payload to execute. |\n| **`editor`** | The Rich Text Editor which will be used by `richText` fields. [More details](../rich-text/overview). |\n| **`db`** \\* | The Database Adapter which will be used by Payload. [More details](../database/overview). |\n| **`serverURL`** | A string used to define the absolute URL of your app. This includes the protocol, for example `https://example.com`. No paths allowed, only protocol, domain and (optionally) port. |\n| **`collections`** | An array of Collections for Payload to manage. [More details](./collections). |\n| **`compatibility`** | Compatibility flags for earlier versions of Payload. [More details](#compatibility-flags). |\n| **`globals`** | An array of Globals for Payload to manage. [More details](./globals). |\n| **`cors`** | Cross-origin resource sharing (CORS) is a mechanism that accept incoming requests from given domains. You can also customize the `Access-Control-Allow-Headers` header. [More details](#cors). |\n| **`localization`** | Opt-in to translate your content into multiple locales. [More details](./localization). |\n| **`logger`** | Logger options, logger options with a destination stream, or an instantiated logger instance. [More details](https://getpino.io/#/docs/api?id=options). |\n| **`loggingLevels`** | An object to override the level to use in the logger for Payload's errors. |\n| **`graphQL`** | Manage GraphQL-specific functionality, including custom queries and mutations, query complexity limits, etc. [More details](../graphql/overview#graphql-options). |\n| **`cookiePrefix`** | A string that will be prefixed to all cookies that Payload sets. |\n| **`csrf`** | A whitelist array of URLs to allow Payload to accept cookies from. [More details](../authentication/overview#csrf-protection). |\n| **`defaultDepth`** | If a user does not specify `depth` while requesting a resource, this depth will be used. [More details](../queries/depth). |\n| **`defaultMaxTextLength`** | The maximum allowed string length to be permitted application-wide. Helps to prevent malicious public document creation. |\n| **`maxDepth`** | The maximum allowed depth to be permitted application-wide. This setting helps prevent against malicious queries. Defaults to `10`. [More details](../queries/depth). |\n| **`indexSortableFields`** | Automatically index all sortable top-level fields in the database to improve sort performance and add database compatibility for Azure Cosmos and similar. |\n| **`upload`** | Base Payload upload configuration. [More details](../upload/overview#payload-wide-upload-options). |\n| **`routes`** | Control the routing structure that Payload binds itself to. [More details](../admin/overview#root-level-routes). |\n| **`email`** | Configure the Email Adapter for Payload to use. [More details](../email/overview). |\n| **`debug`** | Enable to expose more detailed error information. |\n| **`telemetry`** | Disable Payload telemetry by passing `false`. [More details](#telemetry). |\n| **`rateLimit`** | Control IP-based rate limiting for all Payload resources. Used to prevent DDoS attacks, etc. [More details](../production/preventing-abuse#rate-limiting-requests). |\n| **`hooks`** | An array of Root Hooks. [More details](../hooks/overview). |\n| **`plugins`** | An array of Plugins. [More details](../plugins/overview). |\n| **`endpoints`** | An array of Custom Endpoints added to the Payload router. [More details](../rest-api/overview#custom-endpoints). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins). |\n| **`i18n`** | Internationalization configuration. Pass all i18n languages you'd like the admin UI to support. Defaults to English-only. [More details](./i18n). |\n| **`secret`** \\* | A secure, unguessable string that Payload will use for any encryption workflows - for example, password salt / hashing. |\n| **`sharp`** | If you would like Payload to offer cropping, focal point selection, and automatic media resizing, install and pass the Sharp module to the config here. |\n| **`typescript`** | Configure TypeScript settings here. [More details](#typescript). |\n\n_\\* An asterisk denotes that a property is required._\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n Some properties are removed from the client-side bundle. [More details](../admin/components#accessing-the-payload-config).\n\u003c/Banner\u003e\n\n\n### Typescript Config\n\nPayload exposes a variety of TypeScript settings that you can leverage. These settings are used to auto-generate TypeScript interfaces for your [Collections](../configuration/collections) and [Globals](../configuration/globals), and to ensure that Payload uses your [Generated Types](../typescript/overview) for all [Local API](../local-api/overview) methods.\n\nTo customize the TypeScript settings, use the `typescript` property in your Payload Config:\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n typescript: { // highlight-line\n // ...\n }\n})\n```\n\nThe following options are available:\n\n| Option | Description |\n| --------------- | --------------------- |\n| **`autoGenerate`** | By default, Payload will auto-generate TypeScript interfaces for all collections and globals that your config defines. Opt out by setting `typescript.autoGenerate: false`. [More details](../typescript/overview). |\n| **`declare`** | By default, Payload adds a `declare` block to your generated types, which makes sure that Payload uses your generated types for all Local API methods. Opt out by setting `typescript.declare: false`. |\n| **`outputFile`** | Control the output path and filename of Payload's auto-generated types by defining the `typescript.outputFile` property to a full, absolute path. |\n\n## Config Location\n\nFor Payload command-line scripts, we need to be able to locate your Payload Config. We'll check a variety of locations for the presence of `payload.config.ts` by default, including:\n\n1. The root current working directory\n1. The `compilerOptions` in your `tsconfig`*\n1. The `dist` directory*\n\n_\\* Config location detection is different between development and production environments. See below for more details._\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n Ensure your `tsconfig.json` is properly configured for Payload to auto-detect your config location. If if does not exist, or does not specify the proper `compilerOptions`, Payload will default to the current working directory.\n\u003c/Banner\u003e\n\n**Development Mode**\n\nIn development mode, if the configuration file is not found at the root, Payload will attempt to read your `tsconfig.json`, and attempt to find the config file specified in the `rootDir`:\n\n```json\n{\n // ...\n // highlight-start\n \"compilerOptions\": {\n \"rootDir\": \"src\"\n }\n // highlight-end\n}\n```\n\n**Production Mode**\n\nIn production mode, Payload will first attempt to find the config file in the `outDir` of your `tsconfig.json`, and if not found, will fallback to the `rootDir` directory:\n\n```json\n{\n // ...\n // highlight-start\n \"compilerOptions\": {\n \"outDir\": \"dist\",\n \"rootDir\": \"src\"\n }\n // highlight-end\n}\n```\n\nIf none was in either location, Payload will finally check the `dist` directory.\n\n### Customizing the Config Location\n\nIn addition to the above automated detection, you can specify your own location for the Payload Config. This can be useful in situations where your config is not in a standard location, or you wish to switch between multiple configurations. To do this, Payload exposes an [Environment Variable](..environment-variables) to bypass all automatic config detection.\n\nTo use a custom config location, set the `PAYLOAD_CONFIG_PATH` environment variable:\n\n```json\n{\n \"scripts\": {\n \"payload\": \"PAYLOAD_CONFIG_PATH=/path/to/custom-config.ts payload\"\n }\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n `PAYLOAD_CONFIG_PATH` can be either an absolute path, or path relative to your current working directory.\n\u003c/Banner\u003e\n\n## Telemetry\n\nPayload collects **completely anonymous** telemetry data about general usage. This data is super important to us and helps us accurately understand how we're growing and what we can do to build the software into everything that it can possibly be. The telemetry that we collect also help us demonstrate our growth in an accurate manner, which helps us as we seek investment to build and scale our team. If we can accurately demonstrate our growth, we can more effectively continue to support Payload as free and open-source software. To opt out of telemetry, you can pass `telemetry: false` within your Payload Config.\n\nFor more information about what we track, take a look at our [privacy policy](/privacy).\n\n## Cross-origin resource sharing (CORS)\n\nCross-origin resource sharing (CORS) can be configured with either a whitelist array of URLS to allow CORS requests from, a wildcard string (`*`) to accept incoming requests from any domain, or a object with the following properties:\n\n| Option | Description |\n| --------- | --------------------------------------------------------------------------------------------------------------------------------------- |\n| **`origins`** | Either a whitelist array of URLS to allow CORS requests from, or a wildcard string (`'*'`) to accept incoming requests from any domain. |\n| **`headers`** | A list of allowed headers that will be appended in `Access-Control-Allow-Headers`. |\n\nHere's an example showing how to allow incoming requests from any domain:\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n cors: '*' // highlight-line\n})\n```\n\nHere's an example showing how to append a new header (`x-custom-header`) in `Access-Control-Allow-Headers`:\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n // highlight-start\n cors: {\n origins: ['http://localhost:3000']\n headers: ['x-custom-header']\n }\n // highlight-end\n})\n```\n\n## TypeScript\n\nYou can import types from Payload to help make writing your config easier and type-safe. There are two main types that represent the Payload Config, `Config` and `SanitizedConfig`.\n\nThe `Config` type represents a raw Payload Config in its full form. Only the bare minimum properties are marked as required. The `SanitizedConfig` type represents a Payload Config after it has been fully sanitized. Generally, this is only used internally by Payload.\n\n```ts\nimport type { Config, SanitizedConfig } from 'payload'\n```\n\n## Server vs. Client\n\nThe Payload Config only lives on the server and is not allowed to contain any client-side code. That way, you can load up the Payload Config in any server environment or standalone script, without having to use Bundlers or Node.js loaders to handle importing client-only modules (e.g. scss files or React Components) without any errors.\n\nBehind the curtains, the Next.js-based Admin Panel generates a ClientConfig, which strips away any server-only code and enriches the config with React Components.\n\n## Compatibility flags\n\nThe Payload Config can accept compatibility flags for running the newest versions but with older databases. You should only use these flags if you need to, and should confirm that you need to prior to enabling these flags.\n\n`allowLocalizedWithinLocalized`\n\nPayload localization works on a field-by-field basis. As you can nest fields within other fields, you could potentially nest a localized field within a localized field—but this would be redundant and unnecessary. There would be no reason to define a localized field within a localized parent field, given that the entire data structure from the parent field onward would be localized.\n\nBy default, Payload will remove the `localized: true` property from sub-fields if a parent field is localized. Set this compatibility flag to `true` only if you have an existing Payload MongoDB database from pre-3.0, and you have nested localized fields that you would like to maintain without migrating.\n"])</script><script>self.__next_f.push([1,"38:T224c,"])</script><script>self.__next_f.push([1,"\nA Collection is a group of records, called Documents, that all share a common schema. You can define as many Collections as your application needs. Each Document in a Collection is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.\n\nCollections are also used to achieve [Authentication](../authentication/overview) in Payload. By defining a Collection with `auth` options, that Collection receives additional operations to support user authentication.\n\nCollections are the primary way to structure recurring data in your application, such as users, products, pages, posts, and other types of content that you might want to manage. Each Collection can have its own unique [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Admin Options](#admin-options), and more.\n\nTo define a Collection Config, use the `collection` property in your [Payload Config](./overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n collections: [ // highlight-line\n // Your Collections go here\n ],\n})\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n If your Collection is only ever meant to contain a single Document, consider using a [Global](./globals) instead.\n\u003c/Banner\u003e\n\n## Config Options\n\nIt's often best practice to write your Collections in separate files and then import them into the main [Payload Config](../overview).\n\nHere is what a simple Collection Config might look like:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Posts: CollectionConfig = {\n slug: 'posts',\n fields: [\n {\n name: 'title',\n type: 'text',\n }\n ]\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Option | Description |\n| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/collections). |\n| **`access`** | Provide Access Control functions to define exactly who should be able to do what with Documents in this Collection. [More details](../access-control/collections). |\n| **`auth`** | Specify options if you would like this Collection to feature authentication. [More details](../authentication/overview). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`disableDuplicate`** | When true, do not show the \"Duplicate\" button while editing documents within this Collection and prevent `duplicate` from all APIs. |\n| **`defaultSort`** | Pass a top-level field to sort by default in the Collection List View. Prefix the name of the field with a minus symbol (\"-\") to sort in descending order. Multiple fields can be specified by using a string array. |\n| **`dbName`** | Custom table or Collection name depending on the Database Adapter. Auto-generated from slug if not defined. |\n| **`endpoints`** | Add custom routes to the REST API. Set to `false` to disable routes. [More details](../rest-api/overview#custom-endpoints). |\n| **`fields`** \\* | Array of field types that will determine the structure and functionality of the data stored within this Collection. [More details](../fields/overview). |\n| **`graphQL`** | An object with `singularName` and `pluralName` strings used in schema generation. Auto-generated from slug if not defined. Set to `false` to disable GraphQL. |\n| **`hooks`** | Entry point for Hooks. [More details](../hooks/overview#collection-hooks). |\n| **`labels`** | Singular and plural labels for use in identifying this Collection throughout Payload. Auto-generated from slug if not defined. |\n| **`lockDocuments`** | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |\n| **`slug`** \\* | Unique, URL-friendly string that will act as an identifier for this Collection. |\n| **`timestamps`** | Set to false to disable documents' automatically generated `createdAt` and `updatedAt` timestamps. |\n| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |\n| **`upload`** | Specify options if you would like this Collection to support file uploads. For more, consult the [Uploads](../upload/overview) documentation. |\n| **`versions`** | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |\n| **`defaultPopulate`** | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |\n\n_\\* An asterisk denotes that a property is required._\n\n### Fields\n\nFields define the schema of the Documents within a Collection. To learn more, go to the [Fields](../fields/overview) documentation.\n\n### Access Control\n\n[Collection Access Control](../access-control/overview) determines what a user can and cannot do with any given Document within a Collection. To learn more, go to the [Access Control](../access-control/overview) documentation.\n\n### Hooks\n\n[Collection Hooks](../hooks/collections) allow you to tie into the lifecycle of your Documents so you can execute your own logic during specific events. To learn more, go to the [Hooks](../hooks/overview) documentation.\n\n### Admin Options\n\nYou can customize the way that the [Admin Panel](../admin/overview) behaves on a Collection-by-Collection basis. To learn more, go to the [Collection Admin Options](../admin/collections) documentation.\n\n## TypeScript\n\nYou can import types from Payload to help make writing your Collection configs easier and type-safe. There are two main types that represent the Collection Config, `CollectionConfig` and `SanitizeCollectionConfig`.\n\nThe `CollectionConfig` type represents a raw Collection Config in its full form, where only the bare minimum properties are marked as required. The `SanitizedCollectionConfig` type represents a Collection Config after it has been fully sanitized. Generally, this is only used internally by Payload.\n\n```ts\nimport type { CollectionConfig, SanitizedCollectionConfig } from 'payload'\n```\n"])</script><script>self.__next_f.push([1,"39:T1ba4,"])</script><script>self.__next_f.push([1,"\nGlobals are in many ways similar to [Collections](../configuration/collections), except that they correspond to only a single Document. You can define as many Globals as your application needs. Each Global Document is stored in the [Database](../database/overview) based on the [Fields](../fields/overview) that you define, and automatically generates a [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview) used to manage your Documents.\n\nGlobals are the primary way to structure singletons in Payload, such as a header navigation, site-wide banner alerts, or app-wide localized strings. Each Global can have its own unique [Access Control](../access-control/overview), [Hooks](../hooks/overview), [Admin Options](#admin-options), and more.\n\nTo define a Global Config, use the `globals` property in your [Payload Config](./overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n globals: [ // highlight-line\n // Your Globals go here\n ],\n})\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n If you have more than one Global that share the same structure, consider using a [Collection](../configuration/collections) instead.\n\u003c/Banner\u003e\n\n## Config Options\n\nIt's often best practice to write your Globals in separate files and then import them into the main [Payload Config](./overview).\n\nHere is what a simple Global Config might look like:\n\n```ts\nimport { GlobalConfig } from 'payload'\n\nexport const Nav: GlobalConfig = {\n slug: 'nav',\n fields: [\n {\n name: 'items',\n type: 'array',\n required: true,\n maxRows: 8,\n fields: [\n {\n name: 'page',\n type: 'relationship',\n relationTo: 'pages', // \"pages\" is the slug of an existing collection\n required: true,\n },\n ],\n },\n ],\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n For more complex examples, see the [Templates](https://github.com/payloadcms/payload/tree/main/templates) and [Examples](https://github.com/payloadcms/payload/tree/main/examples) directories in the Payload repository.\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Option | Description |\n| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`access`** | Provide Access Control functions to define exactly who should be able to do what with this Global. [More details](../access-control/globals). |\n| **`admin`** | The configuration options for the Admin Panel. [More details](../admin/globals). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`dbName`** | Custom table or collection name for this Global depending on the Database Adapter. Auto-generated from slug if not defined. |\n| **`description`** | Text or React component to display below the Global header to give editors more information. |\n| **`endpoints`** | Add custom routes to the REST API. [More details](../rest-api/overview#custom-endpoints). |\n| **`fields`** \\* | Array of field types that will determine the structure and functionality of the data stored within this Global. [More details](../fields/overview). |\n| **`graphQL.name`** | Text used in schema generation. Auto-generated from slug if not defined. |\n| **`hooks`** | Entry point for Hooks. [More details](../hooks/overview#global-hooks). |\n| **`label`** | Text for the name in the Admin Panel or an object with keys for each language. Auto-generated from slug if not defined. |\n| **`lockDocuments`** | Enables or disables document locking. By default, document locking is enabled. Set to an object to configure, or set to `false` to disable locking. [More details](../admin/locked-documents). |\n| **`slug`** \\* | Unique, URL-friendly string that will act as an identifier for this Global. |\n| **`typescript`** | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |\n| **`versions`** | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#globals-config). |\n\n_\\* An asterisk denotes that a property is required._\n\n### Fields\n\nFields define the schema of the Global. To learn more, go to the [Fields](../fields/overview) documentation.\n\n### Access Control\n\n[Global Access Control](../access-control/globals) determines what a user can and cannot do with any given Global Document. To learn more, go to the [Access Control](../access-control/overview) documentation.\n\n### Hooks\n\n[Global Hooks](../hooks/globals) allow you to tie into the lifecycle of your Documents so you can execute your own logic during specific events. To learn more, go to the [Hooks](../hooks/overview) documentation.\n\n### Admin Options\n\nYou can customize the way that the [Admin Panel](../admin/overview) behaves on a Global-by-Global basis. To learn more, go to the [Global Admin Options](../admin/globals) documentation.\n\n## TypeScript\n\nYou can import types from Payload to help make writing your Global configs easier and type-safe. There are two main types that represent the Global Config, `GlobalConfig` and `SanitizeGlobalConfig`.\n\nThe `GlobalConfig` type represents a raw Global Config in its full form, where only the bare minimum properties are marked as required. The `SanitizedGlobalConfig` type represents a Global Config after it has been fully sanitized. Generally, this is only used internally by Payload.\n\n```ts\nimport type { GlobalConfig, SanitizedGlobalConfig } from 'payload'\n```\n"])</script><script>self.__next_f.push([1,"3a:T219f,"])</script><script>self.__next_f.push([1,"\nThe [Admin Panel](../admin/overview) is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/main/packages/translations). With I18n, editors can navigate the interface and read API error messages in their preferred language. This is similar to [Localization](./localization), but instead of managing translations for the data itself, you are managing translations for your application's interface.\n\nBy default, Payload comes preinstalled with English, but you can easily load other languages into your own application. Languages are automatically detected based on the request. If no language is detected, or if the user's language is not yet supported by your application, English will be chosen.\n\nTo configure I18n, use the `i18n` key in your [Payload Config](./overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n i18n: { // highlight-line\n // ...\n },\n})\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n If there is a language that Payload does not yet support, we accept [code contributions](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md).\n\u003c/Banner\u003e\n\n## Config Options\n\nYou can easily customize and override any of the i18n settings that Payload provides by default. Payload will use your custom options and merge them in with its own.\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n // highlight-start\n i18n: {\n fallbackLanguage: 'en', // default\n }\n // highlight-end\n})\n```\n\nThe following options are available:\n\n| Option | Description |\n| --------------------- | --------------------------------|\n| **`fallbackLanguage`** | The language to fall back to if the user's preferred language is not supported. Default is `'en'`. |\n| **`translations`** | An object containing the translations. The keys are the language codes and the values are the translations. |\n| **`supportedLanguages`** | An object containing the supported languages. The keys are the language codes and the values are the translations. |\n\n## Adding Languages\n\nYou can easily add new languages to your Payload app by providing the translations for the new language. Payload maintains a number of built-in translations that can be imported from `@payloadcms/translations`, but you can also provide your own [Custom Translations](#custom-translations) to support any language.\n\nTo add a new language, use the `i18n.supportedLanguages` key in your [Payload Config](./overview):\n\n```ts\nimport { buildConfig } from 'payload'\nimport { en } from '@payloadcms/translations/languages/en'\nimport { de } from '@payloadcms/translations/languages/de'\n\nexport default buildConfig({\n // ...\n // highlight-start\n i18n: {\n supportedLanguages: { en, de },\n },\n // highlight-end\n})\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n It's best to only support the languages that you need so that the bundled JavaScript is kept to a minimum for your project.\n\u003c/Banner\u003e\n\n### Custom Translations\n\nYou can customize Payload's built-in translations either by extending existing languages or by adding new languages entirely. This can be done by injecting new translation strings into existing languages, or by providing an entirely new language keys altogether.\n\nTo add Custom Translations, use the `i18n.translations` key in your [Payload Config](./overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n //...\n i18n: {\n // highlight-start\n translations: {\n en: {\n custom: {\n // namespace can be anything you want\n key1: 'Translation with {{variable}}', // translation\n },\n // override existing translation keys\n general: {\n dashboard: 'Home',\n },\n },\n },\n // highlight-end\n },\n //...\n})\n```\n\n### Project Translations\n\nWhile Payload's built-in features come fully translated, you may also want to translate parts of your own project. This is possible in places like [Collections](./collections) and [Globals](./globals), such as on their labels and groups, field labels, descriptions or input placeholder text.\n\nTo do this, provide the translations wherever applicable, keyed to the language code:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Articles: CollectionConfig = {\n slug: 'articles',\n labels: {\n singular: {\n // highlight-start\n en: 'Article',\n es: 'Artículo',\n // highlight-end\n },\n plural: {\n // highlight-start\n en: 'Articles',\n es: 'Artículos',\n // highlight-end\n },\n },\n admin: {\n group: {\n // highlight-start\n en: 'Content',\n es: 'Contenido',\n // highlight-end\n },\n },\n fields: [\n {\n name: 'title',\n type: 'text',\n label: {\n // highlight-start\n en: 'Title',\n es: 'Título',\n // highlight-end\n },\n admin: {\n placeholder: {\n // highlight-start\n en: 'Enter title',\n es: 'Introduce el título'\n // highlight-end\n },\n },\n },\n ],\n}\n```\n\n## Node\n\nPayload's backend sets the language on incoming requests before they are handled. This allows backend validation to return error messages in the user's own language or system generated emails to be sent using the correct translation. You can make HTTP requests with the `accept-language` header and Payload will use that language.\n\nAnywhere in your Payload app that you have access to the `req` object, you can access Payload's extensive internationalization features assigned to `req.i18n`. To access text translations you can use `req.t('namespace:key')`.\n\n## TypeScript\n\nIn order to use custom translations in your project, you need to provide the types for the translations.\n\nHere we create a shareable translations object. We will import this in both our custom components and in our Payload config.\n\n```ts\n// \u003crootDir\u003e/custom-translations.ts\n\nimport type { Config } from 'payload'\nimport type { NestedKeysStripped } from '@payloadcms/translations'\n\nexport const customTranslations: Config['i18n']['translations'] = {\n en: {\n general: {\n myCustomKey: 'My custom english translation',\n },\n fields: {\n addLabel: 'Add!',\n }\n },\n}\n\nexport type CustomTranslationsObject = typeof customTranslations.en\nexport type CustomTranslationsKeys = NestedKeysStripped\u003cCustomTranslationsObject\u003e\n```\n\nImport the shared translations object into our Payload config so they are available for use:\n\n```ts\n// \u003crootDir\u003e/payload.config.ts\n\nimport { buildConfig } from 'payload'\n\nimport { customTranslations } from './custom-translations'\n\nexport default buildConfig({\n //...\n i18n: {\n translations: customTranslations,\n },\n //...\n})\n```\n\nImport the shared translation types to use in your [Custom Component](../admin/components):\n\n```ts\n// \u003crootDir\u003e/components/MyComponent.tsx\n\n'use client'\nimport type React from 'react'\nimport { useTranslation } from '@payloadcms/ui'\n\nimport type { CustomTranslationsObject, CustomTranslationsKeys } from '../custom-translations'\n\nexport const MyComponent: React.FC = () =\u003e {\n const { i18n, t } = useTranslation\u003cCustomTranslationsObject, CustomTranslationsKeys\u003e() // These generics merge your custom translations with the default client translations\n\n return t('general:myCustomKey')\n}\n```\n\nAdditionally, Payload exposes the `t` function in various places, for example in labels. Here is how you would type those:\n\n```ts\n// \u003crootDir\u003e/fields/myField.ts\n\nimport type { DefaultTranslationKeys, TFunction } from '@payloadcms/translations'\nimport type { Field } from 'payload'\n\nimport { CustomTranslationsKeys } from '../custom-translations'\n\nconst field: Field = {\n name: 'myField',\n type: 'text',\n label: (\n { t }: { t: TFunction\u003cCustomTranslationsKeys | DefaultTranslationKeys\u003e }, // The generic passed to TFunction does not automatically merge the custom translations with the default translations. We need to merge them ourselves here\n ) =\u003e t('fields:addLabel'),\n}\n```\n\n"])</script><script>self.__next_f.push([1,"3b:T21c2,"])</script><script>self.__next_f.push([1,"\nLocalization is one of the most important features of a modern CMS. It allows you to manage content in multiple languages, then serve it to your users based on their requested language. This is similar to [I18n](./i18n), but instead of managing translations for your application's interface, you are managing translations for the data itself.\n\nWith Localization, you can begin to serve personalized content to your users based on their specific language preferences, such as a multilingual website or multi-site application. There are no limits to the number of locales you can add to your Payload project.\n\nTo configure Localization, use the `localization` key in your [Payload Config](./overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n localization: { // highlight-line\n // ...\n },\n})\n```\n\n## Config Options\n\nAdd the `localization` property to your Payload Config to enable Localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options.\n\nTo configure locales, use the `localization.locales` property in your [Payload Config](./overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n localization: {\n locales: ['en', 'es', 'de'], // required\n defaultLocale: 'en', // required\n },\n})\n```\n\nYou can also define locales using [full configuration objects](#locale-object):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n collections: [\n // collections go here\n ],\n localization: {\n locales: [\n {\n label: 'English',\n code: 'en',\n },\n {\n label: 'Arabic',\n code: 'ar',\n // opt-in to setting default text-alignment on Input fields to rtl (right-to-left)\n // when current locale is rtl\n rtl: true,\n },\n ],\n defaultLocale: 'en', // required\n fallback: true, // defaults to true\n },\n})\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n Localization works very well alongside [I18n](../configuration/i18n).\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Option | Description |\n| -------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| **`locales`** | Array of all the languages that you would like to support. [More details](#locales) |\n| **`defaultLocale`** | Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale. |\n| **`fallback`** | Boolean enabling \"fallback\" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated unless a fallback is explicitly provided in the request. True by default. |\n\n### Locales\n\nThe locales array is a list of all the languages that you would like to support. This can be strings for each language code, or [full configuration objects](#locale-object) for more advanced options.\n\nThe locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 3166‑1) such as `en-US`, `en-UK`, `es-MX`, etc.\n\n#### Locale Object\n\n| Option | Description |\n| -------------------- | ------------------------------------------------------------------------------------------------------------------------------ |\n| **`code`** \\* | Unique code to identify the language throughout the APIs for `locale` and `fallbackLocale` |\n| **`label`** | A string to use for the selector when choosing a language, or an object keyed on the i18n keys for different languages in use. |\n| **`rtl`** | A boolean that when true will make the admin UI display in Right-To-Left. |\n| **`fallbackLocale`** | The code for this language to fallback to when properties of a document are not present. |\n\n_\\* An asterisk denotes that a property is required._\n\n## Field Localization\n\nPayload Localization works on a **field** level—not a document level. In addition to configuring the base Payload Config to support Localization, you need to specify each field that you would like to localize.\n\n**Here is an example of how to enable Localization for a field:**\n\n```js\n{\n name: 'title',\n type: 'text',\n // highlight-start\n localized: true,\n // highlight-end\n}\n```\n\nWith the above configuration, the `title` field will now be saved in the database as an object of all locales instead of a single string.\n\nAll field types with a `name` property support the `localized` property—even the more complex field types like `array`s and `block`s.\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n Enabling Localization for field types that support nested fields will automatically create\n localized \"sets\" of all fields contained within the field. For example, if you have a page layout\n using a blocks field type, you have the choice of either localizing the full layout, by enabling\n Localization on the top-level blocks field, or only certain fields within the layout.\n\u003c/Banner\u003e\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n When converting an existing field to or from `localized: true` the data structure in the document\n will change for this field and so existing data for this field will be lost. Before changing the\n Localization setting on fields with existing data, you may need to consider a field migration\n strategy.\n\u003c/Banner\u003e\n\n## Retrieving Localized Docs\n\nWhen retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be\nused.\n\n#### REST API\n\nREST API locale functionality relies on URL query parameters.\n\n**`?locale=`**\n\nSpecify your desired locale by providing the `locale` query parameter directly in the endpoint URL.\n\n**`?fallback-locale=`**\n\nSpecify fallback locale to be used by providing the `fallback-locale` query parameter. This can be provided as either a\nvalid locale as provided to your base Payload Config, or `'null'`, `'false'`, or `'none'` to disable falling back.\n\n**Example:**\n\n```\nfetch('https://localhost:3000/api/pages?locale=es\u0026fallback-locale=none');\n```\n\n#### GraphQL API\n\nIn the GraphQL API, you can specify `locale` and `fallbackLocale` args to all relevant queries and mutations.\n\nThe `locale` arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum\nvalues (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious\nto see how locales are auto-formatted, you can use the [GraphQL playground](../graphql/overview#graphql-playground).\n\nThe `fallbackLocale` arg will accept valid locales as well as `none` to disable falling back.\n\n**Example:**\n\n```graphql\nquery {\n Posts(locale: de, fallbackLocale: none) {\n docs {\n title\n }\n }\n}\n```\n\n\u003cBanner\u003e\n In GraphQL, specifying the locale at the top level of a query will automatically apply it\n throughout all nested relationship fields. You can override this behavior by re-specifying locale\n arguments in nested related document queries.\n\u003c/Banner\u003e\n\n#### Local API\n\nYou can specify `locale` as well as `fallbackLocale` within the Local API as well as properties on the `options`\nargument. The `locale` property will accept any valid locale, and the `fallbackLocale` property will accept any valid\nlocale as well as `'null'`, `'false'`, `false`, and `'none'`.\n\n**Example:**\n\n```js\nconst posts = await payload.find({\n collection: 'posts',\n locale: 'es',\n fallbackLocale: false,\n})\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n The REST and Local APIs can return all Localization data in one request by passing 'all' or '*' as\n the \u003cstrong\u003elocale\u003c/strong\u003e parameter. The response will be structured so that field values come\n back as the full objects keyed for each locale instead of the single, translated value.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"3c:Tcca,"])</script><script>self.__next_f.push([1,"\nEnvironment Variables are a way to store sensitive information that your application needs to function. This could be anything from API keys to [Database](../database/overview) credentials. Payload allows you to easily use Environment Variables within your config and throughout your application.\n\n## Next.js Applications\n\nIf you are using Next.js, no additional setup is required other than creating your `.env` file.\n\nTo use Environment Variables, add a `.env` file to the root of your project:\n\n```plaintext\nproject-name/\n├─ .env\n├─ package.json\n├─ payload.config.ts\n```\n\nHere is an example of what an `.env` file might look like:\n\n```plaintext\nSERVER_URL=localhost:3000\nDATABASE_URI=mongodb://localhost:27017/my-database\n```\n\nTo use Environment Variables in your Payload Config, you can access them directly from `process.env`:\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n serverURL: process.env.SERVER_URL, // highlight-line\n // ...\n})\n```\n\n## Client-side Environments\n\nFor security and safety reasons, the [Admin Panel](../admin/overview) does **not** include Environment Variables in its _client-side_ bundle by default. But, Next.js provides a mechanism to expose Environment Variables to the client-side bundle when needed.\n\nIf you are building a [Custom Component](../admin/components) and need to access Environment Variables from the client-side, you can do so by prefixing them with `NEXT_PUBLIC_`.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n Be careful about what variables you provide to your client-side code. Analyze every single one to make sure that you're not accidentally leaking sensitive information. Only ever include keys that are safe for the public to read in plain text.\n\u003c/Banner\u003e\n\nFor example, if you've got the following Environment Variable:\n\n```bash\nNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_XXXXXXXXXXXXXXXXXX\n```\n\nThis key will automatically be made available to the client-side Payload bundle and can be referenced in your Custom Component as follows:\n\n```tsx\n'use client'\nimport React from 'react'\n\nconst stripeKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY // highlight-line\n\nconst MyClientComponent = () =\u003e {\n // do something with the key\n\n return (\n \u003cdiv\u003e\n My Client Component\n \u003c/div\u003e\n )\n}\n```\n\nFor more information, check out the [Next.js Documentation](https://nextjs.org/docs/app/building-your-application/configuring/environment-variables).\n\n## Outside of Next.js\n\nIf you are using Payload outside of Next.js, we suggest using the [`dotenv`](https://www.npmjs.com/package/dotenv) package to handle Environment Variables from `.env` files. This will automatically load your Environment Variables into `process.env`.\n\nTo do this, import the package as high up in your application as possible:\n\n```ts\nimport dotenv from 'dotenv'\ndotenv.config() // highlight-line\n\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n serverURL: process.env.SERVER_URL,\n // ...\n})\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n Be sure that `dotenv` can find your `.env` file. By default, it will look for a file named `.env` in the root of your project. If you need to specify a different file, pass the path into the config options.\n\u003c/Banner\u003e\n\n"])</script><script>self.__next_f.push([1,"3d:Teff,"])</script><script>self.__next_f.push([1,"\nPayload is database agnostic, meaning you can use any type of database behind Payload's familiar APIs. Payload is designed to interact with your database through a Database Adapter, which is a thin layer that translates Payload's internal data structures into your database's native data structures.\n\nCurrently, Payload officially supports the following Database Adapters:\n\n- [MongoDB](../database/mongodb) with [Mongoose](https://mongoosejs.com/)\n- [Postgres](../database/postgres) with [Drizzle](https://drizzle.team/)\n- [SQLite](../database/sqlite) with [Drizzle](https://drizzle.team/)\n\nTo configure a Database Adapter, use the `db` property in your [Payload Config](../configuration/overview):\n\n```ts\nimport { buildConfig } from 'payload'\nimport { mongooseAdapter } from '@payloadcms/db-mongodb'\n\nexport default buildConfig({\n // ...\n // highlight-start\n db: mongooseAdapter({\n url: process.env.DATABASE_URI,\n }),\n // highlight-end\n})\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n The Database Adapter is an external dependency and must be installed in your project separately from Payload. You can find the installation instructions for each Database Adapter in their respective documentation.\n\u003c/Banner\u003e\n\n## Selecting a Database\n\nThere are several factors to consider when choosing which database technology and hosting option is right for your project and workload. Payload can theoretically support any database, but it's up to you to decide which database to use.\n\nThere are two main categories of databases to choose from:\n\n- [Non-Relational Databases](#non-relational-databases)\n- [Relational Databases](#relational-databases)\n\n### Non-Relational Databases\n\nIf your project has a lot of dynamic fields, and you are comfortable with allowing Payload to enforce data integrity across your documents, MongoDB is a great choice. With it, your Payload documents are stored as _one_ document in your database—no matter if you have localization enabled, how many block or array fields you have, etc. This means that the shape of your data in your database will very closely reflect your field schema, and there is minimal complexity involved in storing or retrieving your data.\n\nYou should prefer MongoDB if:\n\n- You prefer simplicity within your database\n- You don't want to deal with keeping production / staging databases in sync via [DDL changes](https://en.wikipedia.org/wiki/Data_definition_language)\n- Most (or everything) in your project is [Localized](../configuration/localization)\n- You leverage a lot of [Arrays](../fields/array), [Blocks](../fields/blocks), or `hasMany` [Select](../fields/select) fields\n\n### Relational Databases\n\nMany projects might call for more rigid database architecture where the shape of your data is strongly enforced at the database level. For example, if you know the shape of your data and it's relatively \"flat\", and you don't anticipate it to change often, your workload might suit relational databases like Postgres very well.\n\nYou should prefer a relational DB like Postgres or SQLite if:\n\n- You are comfortable with [Migrations](./migrations)\n- You require enforced data consistency at the database level\n- You have a lot of relationships between collections and require relationships to be enforced\n\n## Payload Differences\n\nIt's important to note that nearly every Payload feature is available in all of our officially supported Database Adapters, including [Localization](../configuration/localization), [Arrays](../fields/array), [Blocks](../fields/blocks), etc. The only thing that is not supported in SQLite yet is the [Point Field](../fields/point), but that should be added soon.\n\nIt's up to you to choose which database you would like to use based on the requirements of your project. Payload has no opinion on which database you should ultimately choose.\n"])</script><script>self.__next_f.push([1,"3e:T2c15,"])</script><script>self.__next_f.push([1,"\nPayload exposes a full suite of migration controls available for your use. Migration commands are accessible via\nthe `npm run payload` command in your project directory.\n\nEnsure you have an npm script called \"payload\" in your `package.json` file.\n\n```json\n{\n \"scripts\": {\n \"payload\": \"cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload\"\n }\n}\n```\n\n\u003cBanner\u003e\n Note that you need to run Payload migrations through the package manager that you are using,\n because Payload should not be globally installed on your system.\n\u003c/Banner\u003e\n\n## Migration file contents\n\nPayload stores all created migrations in a folder that you can specify. By default, migrations are stored\nin `./src/migrations`.\n\nA migration file has two exports - an `up` function, which is called when a migration is executed, and a `down` function\nthat will be called if for some reason the migration fails to complete successfully. The `up` function should contain\nall changes that you attempt to make within the migration, and the `down` should ideally revert any changes you make.\n\nHere is an example migration file:\n\n```ts\nimport { MigrateUpArgs, MigrateDownArgs } from '@payloadcms/your-db-adapter'\n\nexport async function up({ payload, req }: MigrateUpArgs): Promise\u003cvoid\u003e {\n // Perform changes to your database here.\n // You have access to `payload` as an argument, and\n // everything is done in TypeScript.\n}\n\nexport async function down({ payload, req }: MigrateDownArgs): Promise\u003cvoid\u003e {\n // Do whatever you need to revert changes if the `up` function fails\n}\n```\n\n## Using Transactions\n\nWhen migrations are run, each migration is performed in a new [transactions](../database/transactions) for you. All\nyou need to do is pass the `req` object to any [local API](../local-api/overview) or direct database calls, such as\n`payload.db.updateMany()`, to make database changes inside the transaction. Assuming no errors were thrown, the transaction is committed\nafter your `up` or `down` function runs. If the migration errors at any point or fails to commit, it is caught and the\ntransaction gets aborted. This way no change is made to the database if the migration fails.\n\n## Migrations Directory\n\nEach DB adapter has an optional property `migrationDir` where you can override where you want your migrations to be\nstored/read. If this is not specified, Payload will check the default and possibly make a best effort to find your\nmigrations directory by searching in common locations ie. `./src/migrations`, `./dist/migrations`, `./migrations`, etc.\n\nAll database adapters should implement similar migration patterns, but there will be small differences based on the\nadapter and its specific needs. Below is a list of all migration commands that should be supported by your database\nadapter.\n\n## Commands\n\n### Migrate\n\nThe `migrate` command will run any migrations that have not yet been run.\n\n```text\nnpm run payload migrate\n```\n\n### Create\n\nCreate a new migration file in the migrations directory. You can optionally name the migration that will be created. By\ndefault, migrations will be named using a timestamp.\n\n```text\nnpm run payload migrate:create optional-name-here\n```\n\n### Status\n\nThe `migrate:status` command will check the status of migrations and output a table of which migrations have been run,\nand which migrations have not yet run.\n\n`payload migrate:status`\n\n```text\nnpm run payload migrate:status\n```\n\n### Down\n\nRoll back the last batch of migrations.\n\n```text\nnpm run payload migrate:down\n```\n\n### Refresh\n\nRoll back all migrations that have been run, and run them again.\n\n```text\nnpm run payload migrate:refresh\n```\n\n### Reset\n\nRoll back all migrations.\n\n```text\nnpm run payload migrate:reset\n```\n\n### Fresh\n\nDrops all entities from the database and re-runs all migrations from scratch.\n\n```text\nnpm run payload migrate:fresh\n```\n\n## When to run migrations\n\nDepending on which Database Adapter you use, your migration workflow might differ subtly.\n\nIn relational databases, migrations will be **required** for non-development database environments. But with MongoDB, you might only need to run migrations once in a while (or never even need them).\n\n#### MongoDB\n\nIn MongoDB, you'll only ever really need to run migrations for times where you change your database shape, and you have lots of existing data that you'd like to transform from Shape A to Shape B.\n\nIn this case, you can create a migration by running `pnpm payload migrate:create`, and then write the logic that you need to perform to migrate your documents to their new shape. You can then either run your migrations in CI before you build / deploy, or you can run them locally, against your production database, by using your production database connection string on your local computer and running the `pnpm payload migrate` command.\n\n#### Postgres\n\nIn relational databases like Postgres, migrations are a bit more important, because each time you add a new field or a new collection, you'll need to update the shape of your database to match your Payload Config (otherwise you'll see errors upon trying to read / write your data).\n\nThat means that Postgres users of Payload should become familiar with the entire migration workflow from top to bottom.\n\nHere is an overview of a common workflow for working locally against a development database, creating migrations, and then running migrations against your production database before deploying.\n\n**1 - work locally using push mode**\n\nPayload uses Drizzle ORM's powerful `push` mode to automatically sync data changes to your database for you while in development mode. By default, this is enabled and is the suggested workflow to using Postgres and Payload while doing local development.\n\nYou can disable this setting and solely use migrations to manage your local development database (pass `push: false` to your Postgres adapter), but if you do disable it, you may see frequent errors while running development mode. This is because Payload will have updated to your new data shape, but your local database will not have updated.\n\nFor this reason, we suggest that you leave `push` as its default setting and treat your local dev database as a sandbox.\n\nFor more information about push mode and prototyping in development, [click here](./postgres#prototyping-in-dev-mode).\n\nThe typical workflow in Payload is to build out your Payload configs, install plugins, and make progress in development mode - allowing Drizzle to push your changes to your local database for you. Once you're finished, you can create a migration.\n\nBut importantly, you do not need to run migrations against your development database, because Drizzle will have already pushed your changes to your database for you.\n\n\u003cBanner type=\"warning\"\u003e\n Warning: do not mix \"push\" and migrations with your local development database. If you use \"push\"\n locally, and then try to migrate, Payload will throw a warning, telling you that these two methods\n are not meant to be used interchangeably.\n\u003c/Banner\u003e\n\n**2 - create a migration**\n\nOnce you're done with working in your Payload Config, you can create a migration. It's best practice to try and complete a specific task or fully build out a feature before you create a migration.\n\nBut once you're ready, you can run `pnpm payload migrate:create`, which will perform the following steps for you:\n\n- We will look for any existing migrations, and automatically generate SQL changes necessary to convert your schema from its prior state to the new state of your Payload Config\n- We will then create a new migration file in your `/migrations` folder that contains all the SQL necessary to be run\n\nWe won't immediately run this migration for you, however.\n\n\u003cBanner type=\"success\"\u003e\n Tip: migrations created by Payload are relatively programmatic in nature, so there should not be any surprises, but before you check in the created migration it's a good idea to always double-check the contents of the migration files.\n\u003c/Banner\u003e\n\n**3 - set up your build process to run migrations**\n\nGenerally, you want to run migrations before you build Payload for production. This typically happens in your CI pipeline and can usually be configured on platforms like Payload Cloud, Vercel, or Netlify by specifying your build script.\n\nA common set of scripts in a `package.json`, set up to run migrations in CI, might look like this:\n\n```js\n \"scripts\": {\n // For running in dev mode\n \"dev\": \"next dev --turbo\",\n\n // To build your Next + Payload app for production\n \"build\": \"next build\",\n\n // A \"tie-in\" to Payload's CLI for convenience\n // this helps you run `pnpm payload migrate:create` and similar\n \"payload\": \"cross-env NODE_OPTIONS=--no-deprecation payload\",\n\n // This command is what you'd set your `build script` to.\n // Notice how it runs `payload migrate` and then `pnpm build`?\n // This will run all migrations for you before building, in your CI,\n // against your production database\n \"ci\": \"payload migrate \u0026\u0026 pnpm build\",\n },\n```\n\nIn the example above, we've specified a `ci` script which we can use as our \"build script\" in the platform that we are deploying to production with.\n\nThis will require that your build pipeline can connect to your database, and it will simply run the `payload migrate` command prior to starting the build process. By calling `payload migrate`, Payload will automatically execute any migrations in your `/migrations` folder that have not yet been executed against your production database, in the order that they were created.\n\nIf it fails, the deployment will be rejected. But now, with your build script set up to run your migrations, you will be all set! Next time you deploy, your CI will execute the required migrations for you, and your database will be caught up with the shape that your Payload Config requires.\n\n## Running migrations in production\n\nIn certain cases, you might want to run migrations at runtime when the server starts. Running them during build time may be impossible due to not having access to your database connection while building or similar reasoning.\n\nIf you're using a long-running server or container where your Node server starts up one time and then stays initialized, you might prefer to run migrations on server startup instead of within your CI.\n\nIn order to run migrations at runtime, on initialization, you can pass your migrations to your database adapter under the `prodMigrations` key as follows:\n\n```ts\n// Import your migrations from the `index.ts` file\n// that Payload generates for you\nimport { migrations } from './migrations'\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // your config here\n db: postgresAdapter({\n // your adapter config here\n prodMigrations: migrations\n })\n})\n```\n\nPassing your migrations as shown above will tell Payload, in production only, to execute any migrations that need to be run prior to completing the initialization of Payload. This is ideal for long-running services where Payload will only be initialized at startup.\n\n\u003cBanner type=\"warning\"\u003e\n Warning - if Payload is instructed to run migrations in production, this may slow down serverless cold starts on platforms such as Vercel. Generally, this option should only be used for long-running servers / containers.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"3f:T1366,"])</script><script>self.__next_f.push([1,"\nDatabase transactions allow your application to make a series of database changes in an all-or-nothing commit. Consider an HTTP request that creates a new **Order** and has an `afterChange` hook to update the stock count of related **Items**. If an error occurs when updating an **Item** and an HTTP error is returned to the user, you would not want the new **Order** to be persisted or any other items to be changed either. This kind of interaction with the database is handled seamlessly with transactions.\n\nBy default, Payload will use transactions for all data changing operations, as long as it is supported by the configured database. Database changes are contained within all Payload operations and any errors thrown will result in all changes being rolled back without being committed. When transactions are not supported by the database, Payload will continue to operate as expected without them.\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n MongoDB requires a connection to a replicaset in order to make use of transactions.\n\u003c/Banner\u003e\n\nThe initial request made to Payload will begin a new transaction and attach it to the `req.transactionID`. If you have a `hook` that interacts with the database, you can opt in to using the same transaction by passing the `req` in the arguments. For example:\n\n```ts\nconst afterChange: CollectionAfterChangeHook = async ({ req }) =\u003e {\n // because req.transactionID is assigned from Payload and passed through,\n // my-slug will only persist if the entire request is successful\n await req.payload.create({\n req,\n collection: 'my-slug',\n data: {\n some: 'data',\n },\n })\n}\n```\n\n## Async Hooks with Transactions\n\nSince Payload hooks can be async and be written to not await the result, it is possible to have an incorrect success response returned on a request that is rolled back. If you have a hook where you do not `await` the result, then you should **not** pass the `req.transactionID`.\n\n```ts\nconst afterChange: CollectionAfterChangeHook = async ({ req }) =\u003e {\n // WARNING: an async call made with the same req, but NOT awaited,\n // may fail resulting in an OK response being returned with response data that is not committed\n const dangerouslyIgnoreAsync = req.payload.create({\n req,\n collection: 'my-slug',\n data: {\n some: 'other data',\n },\n })\n\n // Should this call fail, it will not rollback other changes\n // because the req (and its transactionID) is not passed through\n const safelyIgnoredAsync = req.payload.create({\n collection: 'my-slug',\n data: {\n some: 'other data',\n },\n })\n}\n```\n\n## Direct Transaction Access\n\nWhen writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database outside of Payload's local API.\n\nThe following functions can be used for managing transactions:\n\n- `payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls.\n- `payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.\n- `payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.\n\nPayload uses the `req` object to pass the transaction ID through to the database adapter. If you are not using the `req` object, you can make a new object to pass the transaction ID directly to database adapter methods and local API calls.\nExample:\n\n```ts\nimport payload from 'payload'\nimport config from './payload.config'\n\nconst standalonePayloadScript = async () =\u003e {\n // initialize Payload\n await payload.init({ config })\n\n const transactionID = await payload.db.beginTransaction()\n\n try {\n // Make an update using the local API\n await payload.update({\n collection: 'posts',\n data: {\n some: 'data',\n },\n where: {\n slug: { equals: 'my-slug' }\n },\n req: { transactionID },\n })\n\n /*\n You can make additional db changes or run other functions\n that need to be committed on an all or nothing basis\n */\n\n // Commit the transaction\n await payload.db.commitTransaction(transactionID)\n } catch (error) {\n // Rollback the transaction\n await payload.db.rollbackTransaction(transactionID)\n }\n}\n\nstandalonePayloadScript()\n```\n\n## Disabling Transactions\n\nIf you wish to disable transactions entirely, you can do so by passing `false` as the `transactionOptions` in your database adapter configuration. All the official Payload database adapters support this option.\n\nIn addition to allowing database transactions to be disabled at the adapter level. You can prevent Payload from using a transaction in direct calls to the local API by adding `disableTransaction: true` to the args. For example:\n\n```ts\nawait payload.update({\n collection: 'posts',\n data: {\n some: 'data',\n },\n where: {\n slug: { equals: 'my-slug' }\n },\n disableTransaction: true,\n})\n```\n"])</script><script>self.__next_f.push([1,"40:T959,"])</script><script>self.__next_f.push([1,"\nTo use Payload with MongoDB, install the package `@payloadcms/db-mongodb`. It will come with everything you need to\nstore your Payload data in MongoDB.\n\nThen from there, pass it to your Payload Config as follows:\n\n```ts\nimport { mongooseAdapter } from '@payloadcms/db-mongodb'\n\nexport default buildConfig({\n // Your config goes here\n collections: [\n // Collections go here\n ],\n // Configure the Mongoose adapter here\n db: mongooseAdapter({\n // Mongoose-specific arguments go here.\n // URL is required.\n url: process.env.DATABASE_URI,\n }),\n})\n```\n\n## Options\n\n| Option | Description |\n| -------------------- | ----------- |\n| `autoPluralization` | Tell Mongoose to auto-pluralize any collection names if it encounters any singular words used as collection `slug`s. |\n| `connectOptions` | Customize MongoDB connection options. Payload will connect to your MongoDB database using default options which you can override and extend to include all the [options](https://mongoosejs.com/docs/connections.html#options) available to mongoose. |\n| `disableIndexHints` | Set to true to disable hinting to MongoDB to use 'id' as index. This is currently done when counting documents for pagination, as it increases the speed of the count function used in that query. Disabling this optimization might fix some problems with AWS DocumentDB. Defaults to false |\n| `migrationDir` | Customize the directory that migrations are stored. |\n| `transactionOptions` | An object with configuration properties used in [transactions](https://www.mongodb.com/docs/manual/core/transactions/) or `false` which will disable the use of transactions. |\n| `collation` | Enable language-specific string comparison with customizable options. Available on MongoDB 3.4+. Defaults locale to \"en\". Example: `{ strength: 3 }`. For a full list of collation options and their definitions, see the [MongoDB documentation](https://www.mongodb.com/docs/manual/reference/collation/). |\n\n## Access to Mongoose models\n\nAfter Payload is initialized, this adapter exposes all of your Mongoose models and they are available for you to work\nwith directly.\n\nYou can access Mongoose models as follows:\n\n- Collection models - `payload.db.collections[myCollectionSlug]`\n- Globals model - `payload.db.globals`\n- Versions model (both collections and globals) - `payload.db.versions[myEntitySlug]`\n"])</script><script>self.__next_f.push([1,"41:T257a,"])</script><script>self.__next_f.push([1,"\nTo use Payload with Postgres, install the package `@payloadcms/db-postgres`. It leverages Drizzle ORM and `node-postgres` to interact with a Postgres database that you provide.\n\nAlternatively, the `@payloadcms/db-vercel-postgres` package is also available and is optimized for use with Vercel.\n\nIt automatically manages changes to your database for you in development mode, and exposes a full suite of migration controls for you to leverage in order to keep other database environments in sync with your schema. DDL transformations are automatically generated.\n\nTo configure Payload to use Postgres, pass the `postgresAdapter` to your Payload Config as follows:\n\n### Usage\n\n`@payloadcms/db-postgres`:\n\n```ts\nimport { postgresAdapter } from '@payloadcms/db-postgres'\n\nexport default buildConfig({\n // Configure the Postgres adapter here\n db: postgresAdapter({\n // Postgres-specific arguments go here.\n // `pool` is required.\n pool: {\n connectionString: process.env.DATABASE_URI,\n },\n }),\n})\n```\n\n`@payloadcms/db-vercel-postgres`:\n\n```ts\nimport { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'\n\nexport default buildConfig({\n // Automatically uses proces.env.POSTGRES_URL if no options are provided.\n db: vercelPostgresAdapter(),\n // Optionally, can accept the same options as the @vercel/postgres package.\n db: vercelPostgresAdapter({\n pool: {\n connectionString: process.env.DATABASE_URL\n },\n }),\n})\n```\n\n## Options\n\n| Option | Description |\n| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `pool` \\* | [Pool connection options](https://orm.drizzle.team/docs/quick-postgresql/node-postgres) that will be passed to Drizzle and `node-postgres` or to `@vercel/postgres` |\n| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |\n| `migrationDir` | Customize the directory that migrations are stored. |\n| `schemaName` (experimental) | A string for the postgres schema to use, defaults to 'public'. |\n| `idType` | A string of 'serial', or 'uuid' that is used for the data type given to id columns. |\n| `transactionOptions` | A PgTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |\n| `disableCreateDatabase` | Pass `true` to disable auto database creation if it doesn't exist. Defaults to `false`. |\n| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '_locales'. |\n| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. |\n| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |\n| `beforeSchemaInit` | Drizzle schema hook. Runs before the schema is built. [More Details](#beforeschemainit) |\n| `afterSchemaInit` | Drizzle schema hook. Runs after the schema is built. [More Details](#afterschemainit) |\n\n## Access to Drizzle\n\nAfter Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.\n\nYou can access Drizzle as follows:\n\n```text\npayload.db.drizzle\n```\n\n## Tables, relations, and enums\n\nIn addition to exposing Drizzle directly, all of the tables, Drizzle relations, and enum configs are exposed for you via the `payload.db` property as well.\n\n- Tables - `payload.db.tables`\n- Enums - `payload.db.enums`\n- Relations - `payload.db.relations`\n\n## Prototyping in development mode\n\nDrizzle exposes two ways to work locally in development mode.\n\nThe first is [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push), which automatically pushes changes you make to your Payload Config (and therefore, Drizzle schema) to your database so you don't have to manually migrate every time you change your Payload Config. This only works in development mode, and should not be mixed with manually running [`migrate`](../database/migrations) commands.\n\nYou will be warned if any changes that you make will entail data loss while in development mode. Push is enabled by default, but you can opt out if you'd like.\n\nAlternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload Config.\n\n## Migration workflows\n\nIn Postgres, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.\n\nFor more information about migrations, [click here](./migrations#when-to-run-migrations).\n\n## Drizzle schema hooks\n\n### beforeSchemaInit\n\nRuns before the schema is built. You can use this hook to extend your database structure with tables that won't be managed by Payload.\n\n```ts\nimport { postgresAdapter } from '@payloadcms/db-postgres'\nimport { integer, pgTable, serial } from 'drizzle-orm/pg-core'\n\npostgresAdapter({\n beforeSchemaInit: [\n ({ schema, adapter }) =\u003e {\n return {\n ...schema,\n tables: {\n ...schema.tables,\n addedTable: pgTable('added_table', {\n id: serial('id').notNull(),\n }),\n },\n }\n },\n ],\n})\n```\n\nOne use case is preserving your existing database structure when migrating to Payload. By default, Payload drops the current database schema, which may not be desirable in this scenario.\nTo quickly generate the Drizzle schema from your database you can use [Drizzle Introspection](https://orm.drizzle.team/kit-docs/commands#introspect--pull)\nYou should get the `schema.ts` file which may look like this:\n\n```ts\nimport { pgTable, uniqueIndex, serial, varchar, text } from 'drizzle-orm/pg-core'\n\nexport const users = pgTable('users', {\n id: serial('id').primaryKey(),\n fullName: text('full_name'),\n phone: varchar('phone', { length: 256 }),\n})\n\nexport const countries = pgTable(\n 'countries',\n {\n id: serial('id').primaryKey(),\n name: varchar('name', { length: 256 }),\n },\n (countries) =\u003e {\n return {\n nameIndex: uniqueIndex('name_idx').on(countries.name),\n }\n },\n)\n\n```\n\nYou can import them into your config and append to the schema with the `beforeSchemaInit` hook like this:\n\n```ts\nimport { postgresAdapter } from '@payloadcms/db-postgres'\nimport { users, countries } from '../drizzle/schema'\n\npostgresAdapter({\n beforeSchemaInit: [\n ({ schema, adapter }) =\u003e {\n return {\n ...schema,\n tables: {\n ...schema.tables,\n users,\n countries\n },\n }\n },\n ],\n})\n```\n\nMake sure Payload doesn't overlap table names with its collections. For example, if you already have a collection with slug \"users\", you should either change the slug or `dbName` to change the table name for this collection. \n\n\n### afterSchemaInit\n\nRuns after the Drizzle schema is built. You can use this hook to modify the schema with features that aren't supported by Payload, or if you want to add a column that you don't want to be in the Payload config.\nTo extend a table, Payload exposes `extendTable` utillity to the args. You can refer to the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration).\nThe following example adds the `extra_integer_column` column and a composite index on `country` and `city` columns.\n\n```ts\nimport { postgresAdapter } from '@payloadcms/db-postgres'\nimport { index, integer } from 'drizzle-orm/pg-core'\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n collections: [\n {\n slug: 'places',\n fields: [\n {\n name: 'country',\n type: 'text',\n },\n {\n name: 'city',\n type: 'text',\n },\n ],\n },\n ],\n db: postgresAdapter({\n afterSchemaInit: [\n ({ schema, extendTable, adapter }) =\u003e {\n extendTable({\n table: schema.tables.places,\n columns: {\n extraIntegerColumn: integer('extra_integer_column'),\n },\n extraConfig: (table) =\u003e ({\n country_city_composite_index: index('country_city_composite_index').on(\n table.country,\n table.city,\n ),\n }),\n })\n\n return schema\n },\n ],\n }),\n})\n\n```\n"])</script><script>self.__next_f.push([1,"42:T21a0,"])</script><script>self.__next_f.push([1,"\nTo use Payload with SQLite, install the package `@payloadcms/db-sqlite`. It leverages Drizzle ORM and `libSQL` to interact with a SQLite database that you provide.\n\nIt automatically manages changes to your database for you in development mode, and exposes a full suite of migration controls for you to leverage in order to keep other database environments in sync with your schema. DDL transformations are automatically generated.\n\nTo configure Payload to use SQLite, pass the `sqliteAdapter` to your Payload Config as follows:\n\n```ts\nimport { sqliteAdapter } from '@payloadcms/db-sqlite'\n\nexport default buildConfig({\n // Your config goes here\n collections: [\n // Collections go here\n ],\n // Configure the SQLite adapter here\n db: sqliteAdapter({\n // SQLite-specific arguments go here.\n // `client.url` is required.\n client: {\n url: process.env.DATABASE_URL,\n authToken: process.env.DATABASE_AUTH_TOKEN,\n }\n }),\n})\n```\n\n## Options\n\n| Option | Description |\n| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `client` \\* | [Client connection options](https://orm.drizzle.team/docs/get-started-sqlite#turso) that will be passed to `createClient` from `@libsql/client`. |\n| `push` | Disable Drizzle's [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push) in development mode. By default, `push` is enabled for development mode only. |\n| `migrationDir` | Customize the directory that migrations are stored. |\n| `logger` | The instance of the logger to be passed to drizzle. By default Payload's will be used. |\n| `transactionOptions` | A SQLiteTransactionConfig object for transactions, or set to `false` to disable using transactions. [More details](https://orm.drizzle.team/docs/transactions) |\n| `localesSuffix` | A string appended to the end of table names for storing localized fields. Default is '_locales'. |\n| `relationshipsSuffix` | A string appended to the end of table names for storing relationships. Default is '_rels'. |\n| `versionsSuffix` | A string appended to the end of table names for storing versions. Defaults to '_v'. |\n| `beforeSchemaInit` | Drizzle schema hook. Runs before the schema is built. [More Details](#beforeschemainit) |\n| `afterSchemaInit` | Drizzle schema hook. Runs after the schema is built. [More Details](#afterschemainit) |\n\n## Access to Drizzle\n\nAfter Payload is initialized, this adapter will expose the full power of Drizzle to you for use if you need it.\n\nYou can access Drizzle as follows:\n\n```text\npayload.db.drizzle\n```\n\n## Tables and relations\n\nIn addition to exposing Drizzle directly, all of the tables and Drizzle relations are exposed for you via the `payload.db` property as well.\n\n- Tables - `payload.db.tables`\n- Relations - `payload.db.relations`\n\n## Prototyping in development mode\n\nDrizzle exposes two ways to work locally in development mode.\n\nThe first is [`db push`](https://orm.drizzle.team/kit-docs/overview#prototyping-with-db-push), which automatically pushes changes you make to your Payload Config (and therefore, Drizzle schema) to your database so you don't have to manually migrate every time you change your Payload Config. This only works in development mode, and should not be mixed with manually running [`migrate`](../database/migrations) commands.\n\nYou will be warned if any changes that you make will entail data loss while in development mode. Push is enabled by default, but you can opt out if you'd like.\n\nAlternatively, you can disable `push` and rely solely on migrations to keep your local database in sync with your Payload Config.\n\n## Migration workflows\n\nIn SQLite, migrations are a fundamental aspect of working with Payload and you should become familiar with how they work.\n\nFor more information about migrations, [click here](./migrations#when-to-run-migrations).\n\n## Drizzle schema hooks\n\n### beforeSchemaInit\n\nRuns before the schema is built. You can use this hook to extend your database structure with tables that won't be managed by Payload.\n\n```ts\nimport { sqliteAdapter } from '@payloadcms/db-sqlite'\nimport { integer, sqliteTable } from 'drizzle-orm/sqlite-core'\n\nsqliteAdapter({\n beforeSchemaInit: [\n ({ schema, adapter }) =\u003e {\n return {\n ...schema,\n tables: {\n ...schema.tables,\n addedTable: sqliteTable('added_table', {\n id: integer('id').primaryKey({ autoIncrement: true }),\n }),\n },\n }\n },\n ],\n})\n```\n\nOne use case is preserving your existing database structure when migrating to Payload. By default, Payload drops the current database schema, which may not be desirable in this scenario.\nTo quickly generate the Drizzle schema from your database you can use [Drizzle Introspection](https://orm.drizzle.team/kit-docs/commands#introspect--pull)\nYou should get the `schema.ts` file which may look like this:\n\n```ts\nimport { sqliteTable, text, uniqueIndex, integer } from 'drizzle-orm/sqlite-core'\n\nexport const users = sqliteTable('users', {\n id: integer('id').primaryKey({ autoIncrement: true }),\n fullName: text('full_name'),\n phone: text('phone', {length: 256}),\n})\n\nexport const countries = sqliteTable(\n 'countries',\n {\n id: integer('id').primaryKey({ autoIncrement: true }),\n name: text('name', { length: 256 }),\n },\n (countries) =\u003e {\n return {\n nameIndex: uniqueIndex('name_idx').on(countries.name),\n }\n },\n)\n\n```\n\nYou can import them into your config and append to the schema with the `beforeSchemaInit` hook like this:\n\n```ts\nimport { sqliteAdapter } from '@payloadcms/db-sqlite'\nimport { users, countries } from '../drizzle/schema'\n\nsqliteAdapter({\n beforeSchemaInit: [\n ({ schema, adapter }) =\u003e {\n return {\n ...schema,\n tables: {\n ...schema.tables,\n users,\n countries\n },\n }\n },\n ],\n})\n```\n\nMake sure Payload doesn't overlap table names with its collections. For example, if you already have a collection with slug \"users\", you should either change the slug or `dbName` to change the table name for this collection. \n\n\n### afterSchemaInit\n\nRuns after the Drizzle schema is built. You can use this hook to modify the schema with features that aren't supported by Payload, or if you want to add a column that you don't want to be in the Payload config.\nTo extend a table, Payload exposes `extendTable` utillity to the args. You can refer to the [Drizzle documentation](https://orm.drizzle.team/docs/sql-schema-declaration).\nThe following example adds the `extra_integer_column` column and a composite index on `country` and `city` columns.\n\n```ts\nimport { sqliteAdapter } from '@payloadcms/db-sqlite'\nimport { index, integer } from 'drizzle-orm/sqlite-core'\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n collections: [\n {\n slug: 'places',\n fields: [\n {\n name: 'country',\n type: 'text',\n },\n {\n name: 'city',\n type: 'text',\n },\n ],\n },\n ],\n db: sqliteAdapter({\n afterSchemaInit: [\n ({ schema, extendTable, adapter }) =\u003e {\n extendTable({\n table: schema.tables.places,\n columns: {\n extraIntegerColumn: integer('extra_integer_column'),\n },\n extraConfig: (table) =\u003e ({\n country_city_composite_index: index('country_city_composite_index').on(\n table.country,\n table.city,\n ),\n }),\n })\n\n return schema\n },\n ],\n }),\n})\n\n```\n"])</script><script>self.__next_f.push([1,"43:T372c,"])</script><script>self.__next_f.push([1,"\nFields are the building blocks of Payload. They define the schema of the Documents that will be stored in the [Database](../database/overview), as well as automatically generate the corresponding UI within the [Admin Panel](../admin/overview).\n\nThere are many [Field Types](#field-types) to choose from, ranging anywhere from simple text strings to nested arrays and blocks. Most fields save data to the database, while others are strictly presentational. Fields can have [Custom Validations](#validation), [Conditional Logic](../admin/fields#conditional-logic), [Access Control](#field-level-access-control), [Hooks](#field-level-hooks), and so much more.\n\nTo configure fields, use the `fields` property in your [Collection](../configuration/collections) or [Global](../configuration/globals) config:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Page: CollectionConfig = {\n // ...\n fields: [ // highlight-line\n // ...\n ]\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n You can fully customize the appearance and behavior of all fields within the Admin Panel. [More details](../admin/fields).\n\u003c/Banner\u003e\n\n## Field Types\n\nPayload provides a wide variety of built-in Field Types, each with its own unique properties and behaviors that determine which values it can accept, how it is presented in the API, and how it will be rendered in the [Admin Panel](../admin/overview).\n\nTo configure fields, use the `fields` property in your [Collection](../configuration/collections) or [Global](../configuration/globals) config:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Page: CollectionConfig = {\n slug: 'pages',\n // highlight-start\n fields: [\n {\n name: 'field',\n type: 'text',\n }\n ]\n // highlight-end\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n Each field is an object with at least the `type` property. This matches the field to its corresponding Field Type. [More details](#field-options).\n\u003c/Banner\u003e\n\nThere are two main categories of fields in Payload:\n\n- [Data Fields](#data-fields)\n- [Presentational Fields](#presentational-fields)\n\nTo begin writing fields, first determine which [Field Type](#field-types) best supports your application. Then to author your field accordingly using the [Field Options](#field-options) for your chosen field type.\n\n### Data Fields\n\nData Fields are used to store data in the [Database](../database/overview). All Data Fields have a `name` property. This is the key that will be used to store the field's value.\n\nHere are the available Data Fields:\n\n- [Array](./array) - for repeating content, supports nested fields\n- [Blocks](./blocks) - for block-based content, supports nested fields\n- [Checkbox](./checkbox) - saves boolean true / false values\n- [Code](./code) - renders a code editor interface that saves a string\n- [Date](./date) - renders a date picker and saves a timestamp\n- [Email](./email) - ensures the value is a properly formatted email address\n- [Group](./group) - nests fields within a keyed object\n- [JSON](./json) - renders a JSON editor interface that saves a JSON object\n- [Number](./number) - saves numeric values\n- [Point](./point) - for location data, saves geometric coordinates\n- [Radio](./radio) - renders a radio button group that allows only one value to be selected\n- [Relationship](./relationship) - assign relationships to other collections\n- [Rich Text](./rich-text) - renders a fully extensible rich text editor\n- [Select](./select) - renders a dropdown / picklist style value selector\n- [Tabs (Named)](./tabs) - similar to group, but renders nested fields within a tabbed layout\n- [Text](./text) - simple text input that saves a string\n- [Textarea](./textarea) - similar to text, but allows for multi-line input\n- [Upload](./upload) - allows local file and image upload\n\n### Presentational Fields\n\nPresentational Fields do not store data in the database. Instead, they are used to organize and present other fields in the [Admin Panel](../admin/overview), or to add custom UI components.\n\nHere are the available Presentational Fields:\n\n- [Collapsible](../fields/collapsible) - nests fields within a collapsible component\n- [Join](../fields/join) - achieves two-way data binding between fields\n- [Row](../fields/row) - aligns fields horizontally\n- [Tabs (Unnamed)](../fields/tabs) - nests fields within a tabbed layout\n- [UI](../fields/ui) - blank field for custom UI components\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n Don't see a Field Type that fits your needs? You can build your own using a [Custom Field Component](../admin/fields#field).\n\u003c/Banner\u003e\n\n## Field Options\n\nAll fields require at least the `type` property. This matches the field to its corresponding [Field Type](#field-types) to determine its appearance and behavior within the [Admin Panel](../admin/overview). Each Field Type has its own unique set of options based on its own type.\n\nTo set a field's type, use the `type` property in your Field Config:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyField: Field = {\n type: 'text', // highlight-line\n name: 'myField',\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n For a full list of configuration options, see the documentation for each [Field Type](#field-types).\n\u003c/Banner\u003e\n\n### Field Names\n\nAll [Data Fields](#data-fields) require a `name` property. This is the key that will be used to store and retrieve the field's value in the database. This property must be unique amongst this field's siblings.\n\nTo set a field's name, use the `name` property in your Field Config:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyField: Field = {\n type: 'text',\n name: 'myField', // highlight-line\n}\n```\n\nPayload reserves various field names for internal use. Using reserved field names will result in your field being sanitized from the config.\n\nThe following field names are forbidden and cannot be used:\n\n - `__v`\n - `salt`\n - `hash`\n - `file`\n\n### Field-level Hooks\n\nIn addition to being able to define [Hooks](../hooks/overview) on a document-level, you can define extremely granular logic field-by-field.\n\nTo define Field-level Hooks, use the `hooks` property in your Field Config:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyField: Field = {\n type: 'text',\n name: 'myField',\n // highlight-start\n hooks: {\n // ...\n }\n // highlight-end\n}\n```\n\nFor full details on Field-level Hooks, see the [Field Hooks](../hooks/fields) documentation.\n\n### Field-level Access Control\n\nIn addition to being able to define [Access Control](../access-control/overview) on a document-level, you can define extremely granular permissions field-by-field.\n\nTo define Field-level Access Control, use the `access` property in your Field Config:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyField: Field = {\n type: 'text',\n name: 'myField',\n // highlight-start\n access: {\n // ...\n }\n // highlight-end\n}\n```\n\nFor full details on Field-level Access Control, see the [Field Access Control](../access-control/fields) documentation.\n\n### Default Values\n\nFields can be optionally prefilled with initial values. This is used in both the [Admin Panel](../admin/overview) as well as API requests to populate missing or undefined field values during the `create` or `update` operations.\n\nTo set a field's default value, use the `defaultValue` property in your Field Config:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyField: Field = {\n type: 'text',\n name: 'myField',\n defaultValue: 'Hello, World!', // highlight-line\n}\n```\n\nDefault values can be defined as a static value or a function that returns a value. When a `defaultValue` is defined statically, Payload's [Database Adapters](../database/overview) will apply it to the database schema or models.\n\nFunctions can be written to make use of the following argument properties:\n\n- `user` - the authenticated user object\n- `locale` - the currently selected locale string\n\nHere is an example of a `defaultValue` function:\n\n```ts\nimport type { Field } from 'payload'\n\nconst translation: {\n en: 'Written by'\n es: 'Escrito por'\n}\n\nexport const myField: Field = {\n name: 'attribution',\n type: 'text',\n // highlight-start\n defaultValue: ({ user, locale }) =\u003e\n `${translation[locale]} ${user.name}`,\n // highlight-end\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n You can use async `defaultValue` functions to fill fields with data from API requests.\n\u003c/Banner\u003e\n\n### Validation\n\nFields are automatically validated based on their [Field Type](#field-types) and other [Field Options](#field-options) such as `required` or `min` and `max` value constraints. If needed, however, field validations can be customized or entirely replaced by providing your own custom validation functions.\n\nTo set a custom field validation function, use the `validate` property in your Field Config:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyField: Field = {\n type: 'text',\n name: 'myField',\n validate: value =\u003e Boolean(value) || 'This field is required' // highlight-line\n}\n```\n\nCustom validation functions should return either `true` or a `string` representing the error message to display in API responses.\n\nThe following arguments are provided to the `validate` function:\n\n| Argument | Description |\n| -------- | --------------------------------------------------------------------------------------------- |\n| `value` | The value of the field being validated. |\n| `ctx` | An object with additional data and context. [More details](#validation-context) |\n\n#### Validation Context\n\nThe `ctx` argument contains full document data, sibling field data, the current operation, and other useful information such as currently authenticated user:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyField: Field = {\n type: 'text',\n name: 'myField',\n // highlight-start\n validate: (val, { user }) =\u003e\n Boolean(user) || 'You must be logged in to save this field',\n // highlight-end\n}\n```\n\nThe following additional properties are provided in the `ctx` object:\n\n| Property | Description |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------ |\n| `data` | An object containing the full collection or global document currently being edited. |\n| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field. |\n| `operation` | Will be `create` or `update` depending on the UI action or API call. |\n| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation. |\n| `req` | The current HTTP request object. Contains `payload`, `user`, etc. |\n\n#### Reusing Default Field Validations\n\nWhen using custom validation functions, Payload will use yours in place of the default. However, you might want to simply augment the default validation with your own custom logic.\n\nTo reuse default field validations, call them from within your custom validation function:\n\n```ts\nimport { text } from 'payload/shared'\n\nconst field: Field = {\n name: 'notBad',\n type: 'text',\n validate: (val, args) =\u003e {\n if (val === 'bad') return 'This cannot be \"bad\"'\n return text(val, args) // highlight-line\n },\n}\n```\n\n#### Async Field Validations\n\nCustom validation functions can also be asynchronous depending on your needs. This makes it possible to make requests to external services or perform other miscellaneous asynchronous logic.\n\nTo write asynchronous validation functions, use the `async` keyword to define your function:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Orders: CollectionConfig = {\n slug: 'orders',\n fields: [\n {\n name: 'customerNumber',\n type: 'text',\n // highlight-start\n validate: async (val, { operation }) =\u003e {\n if (operation !== 'create') return true\n const response = await fetch(`https://your-api.com/customers/${val}`)\n if (response.ok) return true\n return 'The customer number provided does not match any customers within our records.'\n },\n // highlight-end\n },\n ],\n}\n```\n\n### Admin Options\n\nIn addition to each field's base configuration, you can use the `admin` key to specify traits and properties for fields that will only effect how they are _rendered_ within the [Admin Panel](../admin/overview), such as their appearance or behavior.\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyField: Field = {\n type: 'text',\n name: 'myField',\n admin: { // highlight-line\n // ...\n }\n}\n```\n\nFor full details on Admin Options, see the [Field Admin Options](../admin/fields) documentation.\n\n## Custom ID Fields\n\nAll [Collections](../configuration/collections) automatically generate their own ID field. If needed, you can override this behavior by providing an explicit ID field to your config. This field should either be required or have a hook to generate the ID dynamically.\n\nTo define a custom ID field, add a top-level field with the `name` property set to `id`:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const MyCollection: CollectionConfig = {\n // ...\n fields: [\n {\n name: 'id', // highlight-line\n required: true,\n type: 'number',\n },\n ],\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n The Custom ID Fields can only be of type [`Number`](./number) or [`Text`](./text).\n\n Custom ID fields with type `text` must not contain `/` or `.` characters.\n\u003c/Banner\u003e\n\n## TypeScript\n\nYou can import the Payload `Field` type as well as other common types from the `payload` package. [More details](../typescript/overview).\n\n```ts\nimport type { Field } from 'payload'\n```\n"])</script><script>self.__next_f.push([1,"44:T2abd,"])</script><script>self.__next_f.push([1,"\nThe Array Field is used when you need to have a set of \"repeating\" [Fields](./overview). It stores an array of objects containing fields that you define. These fields can be of any type, including other arrays, to achieve infinitely nested data structures.\n\nArrays are useful for many different types of content from simple to complex, such as:\n\n- A \"slider\" with an image ([upload field](../fields/upload)) and a caption ([text field](../fields/text))\n- Navigational structures where editors can specify nav items containing pages ([relationship field](../fields/relationship)), an \"open in new tab\" [checkbox field](../fields/checkbox)\n- Event agenda \"timeslots\" where you need to specify start \u0026 end time ([date field](../fields/date)), label ([text field](../fields/text)), and Learn More page [relationship](../fields/relationship)\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/array.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/array-dark.png\"\n alt=\"Array field with two Rows in Payload Admin Panel\"\n caption=\"Admin Panel screenshot of an Array field with two Rows\"\n/\u003e\n\nTo create an Array Field, set the `type` to `array` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyArrayField: Field = {\n // ...\n // highlight-start\n type: 'array',\n fields: [\n // ...\n ],\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as the heading in the [Admin Panel](../admin/overview) or an object with keys for each language. Auto-generated from name if not defined. |\n| **`fields`** \\* | Array of field types to correspond to each row of the Array. |\n| **`validate`** | Provide a custom validation function that will be executed on both the [Admin Panel](../admin/overview) and the backend. [More](../fields/overview#validation) |\n| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |\n| **`maxRows`** | A number for the most allowed items during validation when a value is present. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide an array of row data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this Array will be kept, so there is no need to specify each nested field as `localized`. |\n| **`required`** | Require this field to have a value. |\n| **`labels`** | Customize the row labels appearing in the Admin dashboard. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`interfaceName`** | Create a top level, reusable [Typescript interface](../typescript/generating-types#custom-field-interfaces) \u0026 [GraphQL type](../graphql/graphql-schema#custom-field-schemas). |\n| **`dbName`** | Custom table name for the field when using SQL Database Adapter ([Postgres](../database/postgres)). Auto-generated from name if not defined. |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nTo customize the appearance and behavior of the Array Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyArrayField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Array Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Option | Description |\n| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`initCollapsed`** | Set the initial collapsed state |\n| **`components.RowLabel`** | React component to be rendered as the label on the array row. [Example](#example-of-a-custom-rowlabel-component) |\n| **`isSortable`** | Disable order sorting by setting this value to `false` |\n\n## Example\n\nIn this example, we have an Array Field called `slider` that contains a set of fields for a simple image slider. Each row in the array has a `title`, `image`, and `caption`. We also customize the row label to display the title if it exists, or a default label if it doesn't.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'slider', // required\n type: 'array', // required\n label: 'Image Slider',\n minRows: 2,\n maxRows: 10,\n interfaceName: 'CardSlider', // optional\n labels: {\n singular: 'Slide',\n plural: 'Slides',\n },\n fields: [\n // required\n {\n name: 'title',\n type: 'text',\n },\n {\n name: 'image',\n type: 'upload',\n relationTo: 'media',\n required: true,\n },\n {\n name: 'caption',\n type: 'text',\n },\n ],\n admin: {\n components: {\n RowLabel: '/path/to/ArrayRowLabel#ArrayRowLabel',\n },\n },\n },\n ],\n}\n```\n\n### Example of a custom RowLabel component\n\n\n```tsx\n'use client'\n\nimport { useRowLabel } from '@payloadcms/ui'\n\nexport const ArrayRowLabel = () =\u003e {\n const { data, rowNumber } = useRowLabel\u003c{ title?: string }\u003e()\n\n const customLabel = `${data.title || 'Slide'} ${String(rowNumber).padStart(2, '0')} `\n\n return \u003cdiv\u003eCustom Label: {customLabel}\u003c/div\u003e\n}\n```\n"])</script><script>self.__next_f.push([1,"45:T3866,"])</script><script>self.__next_f.push([1,"\nThe Blocks Field is \u003cstrong\u003eincredibly powerful\u003c/strong\u003e, storing an array of objects based on the fields that your define, where each item in the array is a \"block\" with its own unique schema.\n\nBlocks are a great way to create a flexible content model that can be used to build a wide variety of content types, including:\n\n- A layout builder tool that grants editors to design highly customizable page or post layouts. Blocks could include configs such as `Quote`, `CallToAction`, `Slider`, `Content`, `Gallery`, or others.\n- A form builder tool where available block configs might be `Text`, `Select`, or `Checkbox`.\n- Virtual event agenda \"timeslots\" where a timeslot could either be a `Break`, a `Presentation`, or a `BreakoutSession`.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/blocks.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/blocks-dark.png\"\n alt=\"Admin Panel screenshot of add Blocks drawer view\"\n caption=\"Admin Panel screenshot of add Blocks drawer view\"\n/\u003e\n\nTo add a Blocks Field, set the `type` to `blocks` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyBlocksField: Field = {\n // ...\n // highlight-start\n type: 'blocks',\n blocks: [\n // ...\n ],\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as the heading in the Admin Panel or an object with keys for each language. Auto-generated from name if not defined. |\n| **`blocks`** \\* | Array of [block configs](../fields/blocks#block-configs) to be made available to this field. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`minRows`** | A number for the fewest allowed items during validation when a value is present. |\n| **`maxRows`** | A number for the most allowed items during validation when a value is present. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API response or the Admin Panel. |\n| **`defaultValue`** | Provide an array of block data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this field will be kept, so there is no need to specify each nested field as `localized`. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`labels`** | Customize the block row labels appearing in the Admin dashboard. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Blocks Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyBlocksField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Blocks Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Option | Description |\n| ------------------- | ---------------------------------- |\n| **`initCollapsed`** | Set the initial collapsed state |\n| **`isSortable`** | Disable order sorting by setting this value to `false` |\n\n#### Customizing the way your block is rendered in Lexical\n\nIf you're using this block within the [Lexical editor](../lexical/overview), you can also customize how the block is rendered in the Lexical editor itself by specifying custom components.\n\n- `admin.components.Label` - pass a custom React component here to customize the way that the label is rendered for this block\n- `admin.components.Block` - pass a component here to completely override the way the block is rendered in Lexical with your own component\n\nThis is super handy if you'd like to present your editors with a very deliberate and nicely designed block \"preview\" right in your rich text.\n\nFor example, if you have a `gallery` block, you might want to actually render the gallery of images directly in your Lexical block. With the `admin.components.Block` property, you can do exactly that!\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\u003cbr/\u003e\n If you customize the way your block is rendered in Lexical, you can import utility components to easily edit / remove your block - so that you don't have to build all of this yourself.\n\u003c/Banner\u003e\n\nTo import these utility components for one of your custom blocks, you can import the following:\n\n```ts\nimport { \n // Edit block buttons (choose the one that corresponds to your usage)\n // When clicked, this will open a drawer with your block's fields\n // so your editors can edit them\n InlineBlockEditButton,\n BlockEditButton,\n\n // Buttons that will remove this block from Lexical \n // (choose the one that corresponds to your usage)\n InlineBlockRemoveButton,\n BlockRemoveButton,\n\n // The label that should be rendered for an inline block\n InlineBlockLabel,\n \n // The default \"container\" that is rendered for an inline block\n // if you want to re-use it\n InlineBlockContainer,\n\n // The default \"collapsible\" UI that is rendered for a regular block\n // if you want to re-use it\n BlockCollapsible,\n \n} from '@payloadcms/richtext-lexical/client'\n```\n\n## Block Configs\n\nBlocks are defined as separate configs of their own.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n \u003cbr /\u003e\n Best practice is to define each block config in its own file, and then import them into your\n Blocks field as necessary. This way each block config can be easily shared between fields. For\n instance, using the \"layout builder\" example, you might want to feature a few of the same blocks\n in a Post collection as well as a Page collection. Abstracting into their own files trivializes\n their reusability.\n\u003c/Banner\u003e\n\n| Option | Description |\n| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`slug`** \\* | Identifier for this block type. Will be saved on each block as the `blockType` property. |\n| **`fields`** \\* | Array of fields to be stored in this block. |\n| **`labels`** | Customize the block labels that appear in the Admin dashboard. Auto-generated from slug if not defined. |\n| **`imageURL`** | Provide a custom image thumbnail to help editors identify this block in the Admin UI. |\n| **`imageAltText`** | Customize this block's image thumbnail alt text. |\n| **`interfaceName`** | Create a top level, reusable [Typescript interface](../typescript/generating-types#custom-field-interfaces) \u0026 [GraphQL type](../graphql/graphql-schema#custom-field-schemas). |\n| **`graphQL.singularName`** | Text to use for the GraphQL schema name. Auto-generated from slug if not defined. NOTE: this is set for deprecation, prefer `interfaceName`. |\n| **`dbName`** | Custom table name for this block type when using SQL Database Adapter ([Postgres](../database/postgres)). Auto-generated from slug if not defined.\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n\n### Auto-generated data per block\n\nIn addition to the field data that you define on each block, Payload will store two additional properties on each block:\n\n**`blockType`**\n\nThe `blockType` is saved as the slug of the block that has been selected.\n\n**`blockName`**\n\nThe Admin Panel provides each block with a `blockName` field which optionally allows editors to label their blocks for better editability and readability.\n\n## Example\n\n`collections/ExampleCollection.js`\n\n```ts\nimport { Block, CollectionConfig } from 'payload'\n\nconst QuoteBlock: Block = {\n slug: 'Quote', // required\n imageURL: 'https://google.com/path/to/image.jpg',\n imageAltText: 'A nice thumbnail image to show what this block looks like',\n interfaceName: 'QuoteBlock', // optional\n fields: [\n // required\n {\n name: 'quoteHeader',\n type: 'text',\n required: true,\n },\n {\n name: 'quoteText',\n type: 'text',\n },\n ],\n}\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'layout', // required\n type: 'blocks', // required\n minRows: 1,\n maxRows: 20,\n blocks: [\n // required\n QuoteBlock,\n ],\n },\n ],\n}\n```\n\n## TypeScript\n\nAs you build your own Block configs, you might want to store them in separate files but retain typing accordingly. To do so, you can import and use Payload's `Block` type:\n\n```ts\nimport type { Block } from 'payload'\n```\n"])</script><script>self.__next_f.push([1,"46:T115d,"])</script><script>self.__next_f.push([1,"\nThe Checkbox Field saves a boolean in the database.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/checkbox.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/checkbox-dark.png\"\n alt=\"Checkbox field with text field in Payload Admin Panel\"\n caption=\"Admin Panel screenshot of Checkbox field with Text field below\"\n/\u003e\n\nTo add a Checkbox Field, set the `type` to `checkbox` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyCheckboxField: Field = {\n // ...\n type: 'checkbox', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value, will default to false if field is also `required`. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](../admin/fields#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Example\n\nHere is an example of a Checkbox Field in a Collection:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'enableCoolStuff', // required\n type: 'checkbox', // required\n label: 'Click me to see fanciness',\n defaultValue: false,\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"47:T18c8,"])</script><script>self.__next_f.push([1,"\nThe Code Field saves a string in the database, but provides the [Admin Panel](../admin/overview) with a code editor styled interface.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/code.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/code-dark.png\"\n alt=\"Shows a Code field in the Payload Admin Panel\"\n caption=\"This field is using the `monaco-react` editor syntax highlighting.\"\n/\u003e\n\nTo add a Code Field, set the `type` to `code` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyBlocksField: Field = {\n // ...\n type: 'code', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`index`** | Build an [index](../database#overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |\n| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. See below for [more detail](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Code Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyCodeField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Code Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Option | Description |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`language`** | This property can be set to any language listed [here](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages). |\n| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IDiffEditorConstructionOptions.html). |\n\n## Example\n\n`collections/ExampleCollection.ts\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'trackingCode', // required\n type: 'code', // required\n required: true,\n admin: {\n language: 'javascript',\n },\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"48:T1f19,"])</script><script>self.__next_f.push([1,"\nThe JSON Field saves raw JSON to the database and provides the [Admin Panel](../admin/overview) with a code editor styled interface. This is different from the [Code Field](./code) which saves the value as a string in the database.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/json.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/json-dark.png\"\n alt=\"Shows a JSON field in the Payload Admin Panel\"\n caption=\"This field is using the `monaco-react` editor syntax highlighting.\"\n/\u003e\n\nTo add a JSON Field, set the `type` to `json` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyJSONField: Field = {\n // ...\n type: 'json', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`jsonSchema`** | Provide a JSON schema that will be used for validation. [JSON schemas](https://json-schema.org/learn/getting-started-step-by-step) |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the JSON Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyJSONField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe JSON Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Option | Description |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`editorOptions`** | Options that can be passed to the monaco editor, [view the full list](https://microsoft.github.io/monaco-editor/typedoc/variables/editor.EditorOptions.html). |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'customerJSON', // required\n type: 'json', // required\n required: true,\n },\n ],\n}\n```\n## JSON Schema Validation\n\nPayload JSON fields fully support the [JSON schema](https://json-schema.org/) standard. By providing a schema in your field config, the editor will be guided in the admin UI, getting typeahead for properties and their formats automatically. When the document is saved, the default validation will prevent saving any invalid data in the field according to the schema in your config.\n\nIf you only provide a URL to a schema, Payload will fetch the desired schema if it is publicly available. If not, it is recommended to add the schema directly to your config or import it from another file so that it can be implemented consistently in your project.\n\n\n### Local JSON Schema\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'customerJSON', // required\n type: 'json', // required\n jsonSchema: {\n uri: 'a://b/foo.json', // required\n fileMatch: ['a://b/foo.json'], // required\n schema: {\n type: 'object',\n properties: {\n foo: {\n enum: ['bar', 'foobar'],\n }\n },\n },\n },\n\n },\n ],\n}\n// {\"foo\": \"bar\"} or {\"foo\": \"foobar\"} - ok\n// Attempting to create {\"foo\": \"not-bar\"} will throw an error\n```\n\n### Remote JSON Schema\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'customerJSON', // required\n type: 'json', // required\n jsonSchema: {\n uri: 'https://example.com/customer.schema.json', // required\n fileMatch: ['https://example.com/customer.schema.json'], // required\n },\n },\n ],\n}\n// If 'https://example.com/customer.schema.json' has a JSON schema\n// {\"foo\": \"bar\"} or {\"foo\": \"foobar\"} - ok\n// Attempting to create {\"foo\": \"not-bar\"} will throw an error\n```\n"])</script><script>self.__next_f.push([1,"49:Tc7c,"])</script><script>self.__next_f.push([1,"\nThe Collapsible Field is presentational-only and only affects the Admin Panel. By using it, you can place fields within a nice layout component that can be collapsed / expanded.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/collapsible.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/collapsible-dark.png\"\n alt=\"Shows a Collapsible field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Collapsible field\"\n/\u003e\n\nTo add a Collapsible Field, set the `type` to `collapsible` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyCollapsibleField: Field = {\n // ...\n // highlight-start\n type: 'collapsible',\n fields: [\n // ...\n ],\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`label`** \\* | A label to render within the header of the collapsible component. This can be a string, function or react component. Function/components receive `({ data, path })` as args. |\n| **`fields`** \\* | Array of field types to nest within this Collapsible. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Collapsible Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyCollapsibleField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Collapsible Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Option | Description |\n| ------------------- | ------------------------------- |\n| **`initCollapsed`** | Set the initial collapsed state |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n label: ({ data }) =\u003e data?.title || 'Untitled',\n type: 'collapsible', // required\n fields: [\n // required\n {\n name: 'title',\n type: 'text',\n required: true,\n },\n {\n name: 'someTextField',\n type: 'text',\n required: true,\n },\n ],\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"4a:T219f,"])</script><script>self.__next_f.push([1,"\nThe Date Field saves a Date in the database and provides the [Admin Panel](../admin/overview) with a customizable time picker interface.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/date.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/date-dark.png\"\n alt=\"Shows a Date field in the Payload Admin Panel\"\n caption=\"This field is using the `react-datepicker` component for UI.\"\n/\u003e\n\nTo add a Date Field, set the `type` to `date` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyDateField: Field = {\n // ...\n type: 'date', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Date Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyDateField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Date Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Property | Description |\n| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |\n| **`placeholder`** | Placeholder text for the field. |\n| **`date`** | Pass options to customize date field appearance. |\n| **`date.displayFormat`** | Format date to be shown in field **cell**. |\n| **`date.pickerAppearance`** \\* | Determines the appearance of the datepicker: `dayAndTime` `timeOnly` `dayOnly` `monthOnly`. |\n| **`date.monthsToShow`** \\* | Number of months to display max is 2. Defaults to 1. |\n| **`date.minDate`** \\* | Min date value to allow. |\n| **`date.maxDate`** \\* | Max date value to allow. |\n| **`date.minTime`** \\* | Min time value to allow. |\n| **`date.maxTime`** \\* | Max date value to allow. |\n| **`date.overrides`** \\* | Pass any valid props directly to the [react-datepicker](https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md) |\n| **`date.timeIntervals`** \\* | Time intervals to display. Defaults to 30 minutes. |\n| **`date.timeFormat`** \\* | Determines time format. Defaults to `'h:mm aa'`. |\n\n_\\* This property is passed directly to [react-datepicker](https://github.com/Hacker0x01/react-datepicker/blob/master/docs/datepicker.md). ._\n\n### Display Format and Picker Appearance\n\nThese properties only affect how the date is displayed in the UI. The full date is always stored in the format `YYYY-MM-DDTHH:mm:ss.SSSZ` (e.g. `1999-01-01T8:00:00.000+05:00`).\n\n`displayFormat` determines how the date is presented in the field **cell**, you can pass any valid (unicode date format)[https://date-fns.org/v4.1.0/docs/format].\n\n`pickerAppearance` sets the appearance of the **react datepicker**, the options available are `dayAndTime`, `dayOnly`, `timeOnly`, and `monthOnly`. By default, the datepicker will display `dayOnly`.\n\nWhen only `pickerAppearance` is set, an equivalent format will be rendered in the date field cell. To overwrite this format, set `displayFormat`.\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'dateOnly',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'dayOnly',\n displayFormat: 'd MMM yyy',\n },\n },\n },\n {\n name: 'timeOnly',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'timeOnly',\n displayFormat: 'h:mm:ss a',\n },\n },\n },\n {\n name: 'monthOnly',\n type: 'date',\n admin: {\n date: {\n pickerAppearance: 'monthOnly',\n displayFormat: 'MMMM yyyy',\n },\n },\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"4b:T1663,"])</script><script>self.__next_f.push([1,"\nThe Email Field enforces that the value provided is a valid email address.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/email.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/email-dark.png\"\n alt=\"Shows an Email field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of an Email field\"\n/\u003e\n\nTo create an Email Field, set the `type` to `email` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyEmailField: Field = {\n // ...\n type: 'email', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Email Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyEmailField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Email Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Property | Description |\n| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |\n| **`placeholder`** | Set this property to define a placeholder string for the field. |\n| **`autoComplete`** | Set this property to a string that will be used for browser autocomplete. |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'contact', // required\n type: 'email', // required\n label: 'Contact Email Address',\n required: true,\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"4c:T1e8b,"])</script><script>self.__next_f.push([1,"\nThe Group Field allows [Fields](./overview) to be nested under a common property name. It also groups fields together visually in the [Admin Panel](../admin/overview).\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/group.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/group-dark.png\"\n alt=\"Shows a Group field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Group field\"\n/\u003e\n\nTo add a Group Field, set the `type` to `group` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyGroupField: Field = {\n // ...\n // highlight-start\n type: 'group',\n fields: [\n // ...\n ],\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`fields`** \\* | Array of field types to nest within this Group. |\n| **`label`** | Used as a heading in the Admin Panel and to name the generated GraphQL type. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide an object of data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. If enabled, a separate, localized set of all data within this Group will be kept, so there is no need to specify each nested field as `localized`. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`interfaceName`** | Create a top level, reusable [Typescript interface](../typescript/generating-types#custom-field-interfaces) \u0026 [GraphQL type](../graphql/graphql-schema#custom-field-schemas). |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Group Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyGroupField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Group Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Option | Description |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`hideGutter`** | Set this property to `true` to hide this field's gutter within the Admin Panel. The field gutter is rendered as a vertical line and padding, but often if this field is nested within a Group, Block, or Array, you may want to hide the gutter. |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'pageMeta', // required\n type: 'group', // required\n interfaceName: 'Meta', // optional\n fields: [\n // required\n {\n name: 'title',\n type: 'text',\n required: true,\n minLength: 20,\n maxLength: 100,\n },\n {\n name: 'description',\n type: 'textarea',\n required: true,\n minLength: 40,\n maxLength: 160,\n },\n ],\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"4d:T1b2c,"])</script><script>self.__next_f.push([1,"\nThe Number Field stores and validates numeric entry and supports additional numerical validation and formatting features.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/number.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/number-dark.png\"\n alt=\"Shows a Number field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Number field\"\n/\u003e\n\nTo add a Number Field, set the `type` to `number` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyNumberField: Field = {\n // ...\n type: 'number', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`min`** | Minimum value accepted. Used in the default `validation` function. |\n| **`max`** | Maximum value accepted. Used in the default `validation` function. |\n| **`hasMany`** | Makes this field an ordered array of numbers instead of just a single number. |\n| **`minRows`** | Minimum number of numbers in the numbers array, if `hasMany` is set to true. |\n| **`maxRows`** | Maximum number of numbers in the numbers array, if `hasMany` is set to true. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Number Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyNumberField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Number Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Property | Description |\n| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |\n| **`step`** | Set a value for the number field to increment / decrement using browser controls. |\n| **`placeholder`** | Set this property to define a placeholder string for the field. |\n| **`autoComplete`** | Set this property to a string that will be used for browser autocomplete. |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'age', // required\n type: 'number', // required\n required: true,\n admin: {\n step: 1,\n },\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"4e:T18fc,"])</script><script>self.__next_f.push([1,"\nThe Point Field saves a pair of coordinates in the database and assigns an index for location related queries. The data structure in the database matches the GeoJSON structure to represent point. The Payload APIs simplifies the object data to only the [longitude, latitude] location.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/point.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/point-dark.png\"\n alt=\"Shows a Point field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Point field\"\n/\u003e\n\nTo add a Point Field, set the `type` to `point` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyPointField: Field = {\n // ...\n type: 'point', // highlight-line\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n The Point Field currently is not supported in SQLite.\n\u003c/Banner\u003e\n\n## Config\n\n| Option | Description |\n| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Used as a field label in the Admin Panel and to name the generated GraphQL type. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. To support location queries, point index defaults to `2dsphere`, to disable the index set to `false`. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](../admin/fields#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'location',\n type: 'point',\n label: 'Location',\n },\n ],\n}\n```\n\n## Querying - near\n\nIn order to do query based on the distance to another point, you can use the `near` operator. When querying using the near operator, the returned documents will be sorted by nearest first.\n\n## Querying - within\n\nIn order to do query based on whether points are within a specific area defined in GeoJSON, you can use the `within` operator.\nExample:\n```ts\nconst polygon: Point[] = [\n [9.0, 19.0], // bottom-left\n [9.0, 21.0], // top-left\n [11.0, 21.0], // top-right\n [11.0, 19.0], // bottom-right\n [9.0, 19.0], // back to starting point to close the polygon\n]\n\npayload.find({\n collection: \"points\",\n where: {\n point: {\n within: {\n type: 'Polygon',\n coordinates: [polygon],\n },\n },\n },\n})\n```\n\n\n## Querying - intersects\n\nIn order to do query based on whether points intersect a specific area defined in GeoJSON, you can use the `intersects` operator.\nExample:\n```ts\nconst polygon: Point[] = [\n [9.0, 19.0], // bottom-left\n [9.0, 21.0], // top-left\n [11.0, 21.0], // top-right\n [11.0, 19.0], // bottom-right\n [9.0, 19.0], // back to starting point to close the polygon\n]\n\npayload.find({\n collection: \"points\",\n where: {\n point: {\n intersects: {\n type: 'Polygon',\n coordinates: [polygon],\n },\n },\n },\n})\n```\n"])</script><script>self.__next_f.push([1,"4f:T199d,"])</script><script>self.__next_f.push([1,"\nThe Radio Field allows for the selection of one value from a predefined set of possible values and presents a radio group-style set of inputs to the [Admin Panel](../admin/overview).\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/radio.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/radio-dark.png\"\n alt=\"Shows a Radio field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Radio field\"\n/\u003e\n\nTo add a Radio Field, set the `type` to `radio` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyRadioField: Field = {\n // ...\n // highlight-start\n type: 'radio',\n options: [\n // ...\n ]\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`options`** \\* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing an `label` string and a `value` string. |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. The default value must exist within provided values in `options`. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](../database/postgres)). Auto-generated from name if not defined. |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n \u003cbr /\u003e\n Option values should be strings that do not contain hyphens or special characters due to GraphQL\n enumeration naming constraints. Underscores are allowed. If you determine you need your option\n values to be non-strings or contain special characters, they will be formatted accordingly before\n being used as a GraphQL enum.\n\u003c/Banner\u003e\n\n## Admin Options\n\nThe customize the appearance and behavior of the Radio Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyRadioField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Radio Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Property | Description |\n| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |\n| **`layout`** | Allows for the radio group to be styled as a horizontally or vertically distributed list. The default value is `horizontal`. |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'color', // required\n type: 'radio', // required\n options: [\n // required\n {\n label: 'Mint',\n value: 'mint',\n },\n {\n label: 'Dark Gray',\n value: 'dark_gray',\n },\n ],\n defaultValue: 'mint', // The first value in options.\n admin: {\n layout: 'horizontal',\n },\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"50:T40e4,"])</script><script>self.__next_f.push([1,"\nThe Relationship Field is one of the most powerful fields Payload features. It provides for the ability to easily relate documents together.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/relationship.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/relationship-dark.png\"\n alt=\"Shows a relationship field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Relationship field\"\n/\u003e\n\nThe Relationship field is used in a variety of ways, including:\n\n- To add `Product` documents to an `Order` document\n- To allow for an `Order` to feature a `placedBy` relationship to either an `Organization` or `User` collection\n- To assign `Category` documents to `Post` documents\n\nTo add a Relationship Field, set the `type` to `relationship` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyRelationshipField: Field = {\n // ...\n // highlight-start\n type: 'relationship',\n relationTo: 'products',\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`relationTo`** \\* | Provide one or many collection `slug`s to be able to assign relationships to. |\n| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-relationship-options). |\n| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many relations instead of only one. |\n| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with `hasMany`. |\n| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with `hasMany`. |\n| **`maxDepth`** | Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](../getting-started/concepts#field-level-max-depth) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n The [Depth](../queries/depth) parameter can be used to automatically populate related documents that are returned by the API.\n\u003c/Banner\u003e\n\n## Admin Options\n\nThe customize the appearance and behavior of the Relationship Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyRelationshipField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Relationship Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Property | Description |\n| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |\n| **`isSortable`** | Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop (only works when `hasMany` is set to `true`). |\n| **`allowCreate`** | Set to `false` if you'd like to disable the ability to create new documents from within the relationship field. |\n| **`allowEdit`** | Set to `false` if you'd like to disable the ability to edit documents from within the relationship field. |\n| **`sortOptions`** | Define a default sorting order for the options within a Relationship field's dropdown. [More](#sortOptions) |\n\n### Sort Options\n\nYou can specify `sortOptions` in two ways:\n\n**As a string:**\n\nProvide a string to define a global default sort field for all relationship field dropdowns across different\ncollections. You can prefix the field name with a minus symbol (\"-\") to sort in descending order.\n\nExample:\n\n```ts\nsortOptions: 'fieldName',\n```\n\nThis configuration will sort all relationship field dropdowns by `\"fieldName\"` in ascending order.\n\n**As an object :**\n\nSpecify an object where keys are collection slugs and values are strings representing the field names to sort by. This\nallows for different sorting fields for each collection's relationship dropdown.\n\nExample:\n\n```ts\nsortOptions: {\n \"pages\"\n:\n \"fieldName1\",\n \"posts\"\n:\n \"-fieldName2\",\n \"categories\"\n:\n \"fieldName3\"\n}\n```\n\nIn this configuration:\n\n- Dropdowns related to `pages` will be sorted by `\"fieldName1\"` in ascending order.\n- Dropdowns for `posts` will use `\"fieldName2\"` for sorting in descending order (noted by the \"-\" prefix).\n- Dropdowns associated with `categories` will sort based on `\"fieldName3\"` in ascending order.\n\nNote: If `sortOptions` is not defined, the default sorting behavior of the Relationship field dropdown will be used.\n\n## Filtering relationship options\n\nOptions can be dynamically limited by supplying a [query constraint](../queries/overview), which will be used both\nfor validating input and filtering available relationships in the UI.\n\nThe `filterOptions` property can either be a `Where` query, or a function returning `true` to not filter, `false` to\nprevent all, or a `Where` query. When using a function, it will be\ncalled with an argument object with the following properties:\n\n| Property | Description |\n| ------------- | ----------------------------------------------------------------------------------------------------- |\n| `relationTo` | The collection `slug` to filter against, limited to this field's `relationTo` property |\n| `data` | An object containing the full collection or global document currently being edited |\n| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field |\n| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |\n| `user` | An object containing the currently authenticated user |\n\n## Example\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'purchase',\n type: 'relationship',\n relationTo: ['products', 'services'],\n filterOptions: ({ relationTo, siblingData }) =\u003e {\n // returns a Where query dynamically by the type of relationship\n if (relationTo === 'products') {\n return {\n stock: { greater_than: siblingData.quantity },\n }\n }\n\n if (relationTo === 'services') {\n return {\n isAvailable: { equals: true },\n }\n }\n },\n },\n ],\n}\n```\n\nYou can learn more about writing queries [here](../queries/overview).\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n When a relationship field has both \u003cstrong\u003efilterOptions\u003c/strong\u003e and a custom{' '}\n \u003cstrong\u003evalidate\u003c/strong\u003e function, the api will not validate \u003cstrong\u003efilterOptions\u003c/strong\u003e{' '}\n unless you call the default relationship field validation function imported from{' '}\n \u003cstrong\u003epayload/shared\u003c/strong\u003e in your validate function.\n\u003c/Banner\u003e\n\n## Bi-directional relationships\n\nThe `relationship` field on its own is used to define relationships for the document that contains the relationship field, and this can be considered as a \"one-way\" relationship. For example, if you have a Post that has a `category` relationship field on it, the related `category` itself will not surface any information about the posts that have the category set.\n\nHowever, the `relationship` field can be used in conjunction with the `Join` field to produce powerful bi-directional relationship authoring capabilities. If you're interested in bi-directional relationships, check out the [documentation for the Join field](./join).\n\n## How the data is saved\n\nGiven the variety of options possible within the `relationship` field type, the shape of the data needed for creating\nand updating these fields can vary. The following sections will describe the variety of data shapes that can arise from\nthis field.\n\n### Has One\n\nThe most simple pattern of a relationship is to use `hasMany: false` with a `relationTo` that allows for only one type\nof collection.\n\n```ts\n{\n slug: 'example-collection',\n fields\n:\n [\n {\n name: 'owner', // required\n type: 'relationship', // required\n relationTo: 'users', // required\n hasMany: false,\n }\n ]\n}\n```\n\nThe shape of the data to save for a document with the field configured this way would be:\n\n```json\n{\n // ObjectID of the related user\n \"owner\": \"6031ac9e1289176380734024\"\n}\n```\n\nWhen querying documents in this collection via REST API, you could query as follows:\n\n`?where[owner][equals]=6031ac9e1289176380734024`.\n\n### Has One - Polymorphic\n\nAlso known as **dynamic references**, in this configuration, the `relationTo` field is an array of Collection slugs that\ntells Payload which Collections are valid to reference.\n\n```ts\n{\n slug: 'example-collection',\n fields\n:\n [\n {\n name: 'owner', // required\n type: 'relationship', // required\n relationTo: ['users', 'organizations'], // required\n hasMany: false,\n }\n ]\n}\n```\n\nThe shape of the data to save for a document with more than one relationship type would be:\n\n```json\n{\n \"owner\": {\n \"relationTo\": \"organizations\",\n \"value\": \"6031ac9e1289176380734024\"\n }\n}\n```\n\nHere is an example for how to query documents by this data (note the difference in referencing the `owner.value`):\n\n`?where[owner.value][equals]=6031ac9e1289176380734024`.\n\nYou can also query for documents where a field has a relationship to a specific Collection:\n\n`?where[owners.relationTo][equals]=organizations`.\n\nThis query would return only documents that have an owner relationship to organizations.\n\n### Has Many\n\nThe `hasMany` tells Payload that there may be more than one collection saved to the field.\n\n```ts\n{\n slug: 'example-collection',\n fields\n:\n [\n {\n name: 'owners', // required\n type: 'relationship', // required\n relationTo: 'users', // required\n hasMany: true,\n }\n ]\n}\n```\n\nTo save the to `hasMany` relationship field we need to send an array of IDs:\n\n```json\n{\n \"owners\": [\"6031ac9e1289176380734024\", \"602c3c327b811235943ee12b\"]\n}\n```\n\nWhen querying documents, the format does not change for arrays:\n\n`?where[owners][equals]=6031ac9e1289176380734024`.\n\n### Has Many - Polymorphic\n\n```ts\n{\n slug: 'example-collection',\n fields\n:\n [\n {\n name: 'owners', // required\n type: 'relationship', // required\n relationTo: ['users', 'organizations'], // required\n hasMany: true,\n required: true,\n }\n ]\n}\n```\n\nRelationship fields with `hasMany` set to more than one kind of collections save their data as an array of objects—each\ncontaining the Collection `slug` as the `relationTo` value, and the related document `id` for the `value`:\n\n```json\n{\n \"owners\": [\n {\n \"relationTo\": \"users\",\n \"value\": \"6031ac9e1289176380734024\"\n },\n {\n \"relationTo\": \"organizations\",\n \"value\": \"602c3c327b811235943ee12b\"\n }\n ]\n}\n```\n\nQuerying is done in the same way as the earlier Polymorphic example:\n\n`?where[owners.value][equals]=6031ac9e1289176380734024`.\n\n### Querying and Filtering Polymorphic Relationships\n\nPolymorphic and non-polymorphic relationships must be queried differently because of how the related data is stored and\nmay be inconsistent across different collections. Because of this, filtering polymorphic relationship fields from the\nCollection List admin UI is limited to the `id` value.\n\nFor a polymorphic relationship, the response will always be an array of objects. Each object will contain\nthe `relationTo` and `value` properties.\n\nThe data can be queried by the related document ID:\n\n`?where[field.value][equals]=6031ac9e1289176380734024`.\n\nOr by the related document Collection slug:\n\n`?where[field.relationTo][equals]=your-collection-slug`.\n\nHowever, you **cannot** query on any field values within the related document.\nSince we are referencing multiple collections, the field you are querying on may not exist and break the query.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n You \u003cstrong\u003ecannot\u003c/strong\u003e query on a field within a polymorphic relationship as you would with a\n non-polymorphic relationship.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"51:T3202,"])</script><script>self.__next_f.push([1,"\nThe Join Field is used to make Relationship and Upload fields available in the opposite direction. With a Join you can\nedit and view collections\nhaving reference to a specific collection document. The field itself acts as a virtual field, in that no new data is\nstored on the collection with a Join\nfield. Instead, the Admin UI surfaces the related documents for a better editing experience and is surfaced by Payload's\nAPIs.\n\nThe Join field is useful in scenarios including:\n\n- To surface `Order`s for a given `Product`\n- To view and edit `Posts` belonging to a `Category`\n- To work with any bi-directional relationship data\n- Displaying where a document or upload is used in other documents\n\n\u003cLightDarkImage\nsrcLight=\"https://payloadcms.com/images/docs/fields/join.png\"\nsrcDark=\"https://payloadcms.com/images/docs/fields/join-dark.png\"\nalt=\"Shows Join field in the Payload Admin Panel\"\ncaption=\"Admin Panel screenshot of Join field\"\n/\u003e\n\nFor the Join field to work, you must have an existing [relationship](./relationship) or [upload](./upload) field in the\ncollection you are joining. This will reference the collection and path of the field of the related documents.\nTo add a Relationship Field, set the `type` to `join` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyJoinField: Field = {\n // highlight-start\n name: 'relatedPosts',\n type: 'join',\n collection: 'posts',\n on: 'category',\n // highlight-end\n}\n\n// relationship field in another collection:\nexport const MyRelationshipField: Field = {\n name: 'category',\n type: 'relationship',\n relationTo: 'categories',\n}\n```\n\nIn this example, the field is defined to show the related `posts` when added to a `category` collection. The `on`\nproperty is used to specify the relationship field name of the field that relates to the collection document.\n\nWith this example, if you navigate to a Category in the Admin UI or an API response, you'll now see that the Posts which\nare related to the Category are populated for you. This is extremely powerful and can be used to define a wide variety\nof relationship types in an easy manner.\n\n\u003cBanner type=\"success\"\u003e\n The Join field is extremely performant and does not add additional query overhead to your API responses until you add depth of 1 or above. It works in all database adapters. In MongoDB, we use \u003cstrong\u003eaggregations\u003c/strong\u003e to automatically join in related documents, and in relational databases, we use joins.\n\u003c/Banner\u003e\n\n### Schema advice\n\nWhen modeling your database, you might come across many places where you'd like to feature bi-directional relationships.\nBut here's an important consideration—you generally only want to store information about a given relationship in _one_\nplace.\n\nLet's take the Posts and Categories example. It makes sense to define which category a post belongs to while editing the\npost.\n\nIt would generally not be necessary to have a list of post IDs stored directly on the category as well, for a few\nreasons:\n\n- You want to have a \"single source of truth\" for relationships, and not worry about keeping two sources in sync with\n one another\n- If you have hundreds, thousands, or even millions of posts, you would not want to store all of those post IDs on a\n given category\n- Etc.\n\nThis is where the `join` field is especially powerful. With it, you only need to store the `category_id` on the `post`,\nand Payload will automatically join in related posts for you when you query for categories. The related category is only\nstored on the post itself - and is not duplicated on both sides. However, the `join` field is what enables\nbi-directional APIs and UI for you.\n\n### Using the Join field to have full control of your database schema\n\nFor typical polymorphic / many relationships, if you're using Postgres or SQLite, Payload will automatically create\na `posts_rels` table, which acts as a junction table to store all of a given document's relationships.\n\nHowever, this might not be appropriate for your use case if you'd like to have more control over your database\narchitecture. You might not want to have that `_rels` table, and would prefer to maintain / control your own junction\ntable design.\n\n\u003cBanner type=\"success\"\u003e\n With the Join field, you can control your own junction table design, and avoid Payload's automatic _rels table creation.\n\u003c/Banner\u003e\n\nThe `join` field can be used in conjunction with _any_ collection - and if you wanted to define your own \"junction\"\ncollection, which, say, is called `categories_posts` and has a `post_id` and a `category_id` column, you can achieve\ncomplete control over the shape of that junction table.\n\nYou could go a step further and leverage the `admin.hidden` property of the `categories_posts` collection to hide the\ncollection from appearing in the Admin UI navigation.\n\n#### Specifying additional fields on relationships\n\nAnother very powerful use case of the `join` field is to be able to define \"context\" fields on your relationships. Let's\nsay that you have Posts and Categories, and use join fields on both your Posts and Categories collection to join in\nrelated docs from a new pseudo-junction collection called `categories_posts`. Now, the relations are stored in this\nthird junction collection, and can be surfaced on both Posts and Categories. But, importantly, you could add\nadditional \"context\" fields to this shared junction collection.\n\nFor example, on this `categories_posts` collection, in addition to having the `category` and `post` fields, we could add\ncustom \"context\" fields like `featured` or `spotlight`,\nwhich would allow you to store additional information directly on relationships.\nThe `join` field gives you complete control over any type of relational architecture in Payload, all wrapped up in a\npowerful Admin UI.\n\n## Config Options\n\n| Option | Description |\n|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`name`** \\* | To be used as the property name when retrieved from the database. [More](../fields/overview#field-names) |\n| **`collection`** \\* | The `slug`s having the relationship field. |\n| **`on`** \\* | The name of the relationship or upload field that relates to the collection document. Use dot notation for nested paths, like 'myGroup.relationName'. |\n| **`where`** \\* | A `Where` query to hide related documents from appearing. Will be merged with any `where` specified in the request. |\n| **`maxDepth`** | Default is 1, Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached. [Max Depth](../getting-started/concepts#field-level-max-depth). |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`defaultLimit`** | The number of documents to return. Set to 0 to return all related documents. |\n| **`defaultSort`** | The field name used to specify the order the joined documents are returned. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-config-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins). |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema. |\n\n_\\* An asterisk denotes that a property is required._\n\n\n## Admin Config Options\n\nYou can control the user experience of the join field using the `admin` config properties. The following options are supported:\n\n| Option | Description |\n|------------------------|----------------------------------------------------------------------------------------|\n| **`allowCreate`** | Set to `false` to remove the controls for making new related documents from this field. |\n| **`components.Label`** | Override the default Label of the Field Component. [More details](../admin/fields#label) |\n\n## Join Field Data\n\nWhen a document is returned that for a Join field is populated with related documents. The structure returned is an\nobject with:\n\n- `docs` an array of related documents or only IDs if the depth is reached\n- `hasNextPage` a boolean indicating if there are additional documents\n\n```json\n{\n \"id\": \"66e3431a3f23e684075aae9c\",\n \"relatedPosts\": {\n \"docs\": [\n {\n \"id\": \"66e3431a3f23e684075aaeb9\",\n // other fields...\n \"category\": \"66e3431a3f23e684075aae9c\"\n }\n // { ... }\n ],\n \"hasNextPage\": false\n }\n // other fields...\n}\n```\n\n## Query Options\n\nThe Join Field supports custom queries to filter, sort, and limit the related documents that will be returned. In\naddition to the specific query options for each Join Field, you can pass `joins: false` to disable all Join Field from\nreturning. This is useful for performance reasons when you don't need the related documents.\n\nThe following query options are supported:\n\n| Property | Description |\n|-------------|-----------------------------------------------------------------------------------------------------|\n| **`limit`** | The maximum related documents to be returned, default is 10. |\n| **`where`** | An optional `Where` query to filter joined documents. Will be merged with the field `where` object. |\n| **`sort`** | A string used to order related results |\n\nThese can be applied to the local API, GraphQL, and REST API.\n\n### Local API\n\nBy adding `joins` to the local API you can customize the request for each join field by the `name` of the field.\n\n```js\nconst result = await db.findOne('categories', {\n where: {\n title: {\n equals: 'My Category'\n }\n },\n joins: {\n relatedPosts: {\n limit: 5,\n where: {\n title: {\n equals: 'My Post'\n }\n },\n sort: 'title'\n }\n }\n})\n```\n\n### Rest API\n\nThe rest API supports the same query options as the local API. You can use the `joins` query parameter to customize the\nrequest for each join field by the `name` of the field. For example, an API call to get a document with the related\nposts limited to 5 and sorted by title:\n\n`/api/categories/${id}?joins[relatedPosts][limit]=5\u0026joins[relatedPosts][sort]=title`\n\nYou can specify as many `joins` parameters as needed for the same or different join fields for a single request.\n\n### GraphQL\n\nThe GraphQL API supports the same query options as the local and REST APIs. You can specify the query options for each\njoin field in your query.\n\nExample:\n\n```graphql\nquery {\n Categories {\n docs {\n relatedPosts(\n sort: \"createdAt\"\n limit: 5\n where: {\n author: {\n equals: \"66e3431a3f23e684075aaeb9\"\n }\n }\n ) {\n docs {\n title\n }\n hasNextPage\n }\n }\n }\n}\n```\n"])</script><script>self.__next_f.push([1,"52:T17f7,"])</script><script>self.__next_f.push([1,"\nThe Rich Text Field is a powerful way to allow editors to write dynamic content. The content is saved as JSON in the database and can be converted into any format, including HTML, that you need.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/richtext.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/richtext-dark.png\"\n alt=\"Shows a Rich Text field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Rich Text field\"\n/\u003e\n\nPayload's rich text field is built on an \"adapter pattern\" which lets you specify which rich text editor you'd like to use.\n\nRight now, Payload is officially supporting two rich text editors:\n\n1. [SlateJS](../rich-text/slate) - legacy, backwards-compatible with 1.0\n2. [Lexical](../lexical/overview) - recommended\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003e\n Consistent with Payload's goal of making you learn as little of Payload as possible, customizing\n and using the Rich Text Editor does not involve learning how to develop for a{' '}\u003cem\u003ePayload\u003c/em\u003e{' '}rich text editor.\n \u003c/strong\u003e\n\n Instead, you can invest your time and effort into learning the underlying open-source tools that\n will allow you to apply your learnings elsewhere as well.\n\u003c/Banner\u003e\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`editor`** | Override the rich text editor specified in your base configuration for this field. |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Rich Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyRichTextField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Rich Text Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Property | Description |\n| ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |\n| **`placeholder`** | Set this property to define a placeholder string for the field. |\n| **`hideGutter`** | Set this property to `true` to hide this field's gutter within the Admin Panel. |\n| **`rtl`** | Override the default text direction of the Admin Panel for this field. Set to `true` to force right-to-left text direction. |\n\n## Editor-specific Options\n\nFor a ton more editor-specific options, including how to build custom rich text elements directly into your editor, take a look at either the [Slate docs](../rich-text/slate) or the [Lexical docs](../lexical/overview) depending on which editor you're using.\n"])</script><script>self.__next_f.push([1,"53:T8f2,"])</script><script>self.__next_f.push([1,"\nThe Row Field is presentational-only and only affects the [Admin Panel](../admin/overview). By using it, you can arrange [Fields](./overview) next to each other horizontally.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/row.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/row-dark.png\"\n alt=\"Shows a row field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Row field\"\n/\u003e\n\nTo add a Row Field, set the `type` to `row` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyRowField: Field = {\n // ...\n // highlight-start\n type: 'row',\n fields: [\n // ...\n ]\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`fields`** \\* | Array of field types to nest within this Row. |\n| **`admin`** | Admin-specific configuration excluding `description`, `readOnly`, and `hidden`. [More details](../admin/fields#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n type: 'row', // required\n fields: [\n // required\n {\n name: 'label',\n type: 'text',\n required: true,\n admin: {\n width: '50%',\n },\n },\n {\n name: 'value',\n type: 'text',\n required: true,\n admin: {\n width: '50%',\n },\n },\n ],\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"54:T269d,"])</script><script>self.__next_f.push([1,"\nThe Select Field provides a dropdown-style interface for choosing options from a predefined list as an enumeration.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/select.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/select-dark.png\"\n alt=\"Shows a Select field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Select field\"\n/\u003e\n\nTo add a Select Field, set the `type` to `select` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MySelectField: Field = {\n // ...\n // highlight-start\n type: 'select',\n options: [\n // ...\n ]\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`options`** \\* | Array of options to allow the field to store. Can either be an array of strings, or an array of objects containing a `label` string and a `value` string. |\n| **`hasMany`** | Boolean when, if set to `true`, allows this field to have many selections instead of only one. |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. See the [default field admin config](../fields/overview#admin-options) for more details. |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`enumName`** | Custom enum name for this field when using SQL Database Adapter ([Postgres](../database/postgres)). Auto-generated from name if not defined. |\n| **`dbName`** | Custom table name (if `hasMany` set to `true`) for this field when using SQL Database Adapter ([Postgres](../database/postgres)). Auto-generated from name if not defined. |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n Option values should be strings that do not contain hyphens or special characters due to GraphQL\n enumeration naming constraints. Underscores are allowed. If you determine you need your option\n values to be non-strings or contain special characters, they will be formatted accordingly before\n being used as a GraphQL enum.\n\u003c/Banner\u003e\n\n## Admin Options\n\nThe customize the appearance and behavior of the Select Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MySelectField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Select Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Property | Description |\n| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |\n| **`isClearable`** | Set to `true` if you'd like this field to be clearable within the Admin UI. |\n| **`isSortable`** | Set to `true` if you'd like this field to be sortable within the Admin UI using drag and drop. (Only works when `hasMany` is set to `true`) |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'selectedFeatures', // required\n type: 'select', // required\n hasMany: true,\n admin: {\n isClearable: true,\n isSortable: true, // use mouse to drag and drop different values, and sort them according to your choice\n },\n options: [\n {\n label: 'Metallic Paint',\n value: 'metallic_paint',\n },\n {\n label: 'Alloy Wheels',\n value: 'alloy_wheels',\n },\n {\n label: 'Carbon Fiber Dashboard',\n value: 'carbon_fiber_dashboard',\n },\n ],\n },\n ],\n}\n```\n\n## Customization\n\nThe Select field UI component can be customized by providing a custom React component to the `components` object in the Base config.\n\n```ts\nexport const CustomSelectField: Field = {\n name: 'customSelectField',\n type: 'select', // or 'text' if you have dynamic options\n admin: {\n components: {\n Field: CustomSelectComponent({\n options: [\n {\n label: 'Option 1',\n value: '1',\n },\n {\n label: 'Option 2',\n value: '2',\n },\n ],\n }),\n },\n },\n}\n```\n\nYou can import the existing Select component directly from Payload, then extend and customize it as needed.\n\n```ts\nimport * as React from 'react';\nimport { SelectInput, useAuth, useField } from '@payloadcms/ui';\n\ntype CustomSelectProps = {\n path: string;\n options: {\n label: string;\n value: string;\n }[];\n}\n\nexport const CustomSelectComponent: React.FC\u003cCustomSelectProps\u003e = ({ path, options }) =\u003e {\n const { value, setValue } = useField\u003cstring\u003e({ path })\n const { user } = useAuth()\n\n const adjustedOptions = options.filter((option) =\u003e {\n /*\n A common use case for a custom select\n is to show different options based on\n the current user's role.\n */\n return option;\n });\n\n return (\n \u003cdiv\u003e\n \u003clabel className=\"field-label\"\u003e\n Custom Select\n \u003c/label\u003e\n \u003cSelectInput\n path={path}\n name={path}\n options={adjustedOptions}\n value={value}\n onChange={(e) =\u003e setValue(e.value)}\n /\u003e\n \u003c/div\u003e\n )\n}\n```\n\nIf you are looking to create a dynamic select field, the following tutorial will walk you through the process of creating a custom select field that fetches its options from an external API.\n\n\u003cVideoDrawer\n id=\"Efn9OxSjA6Y\"\n label=\"How to Create a Custom Select Field\"\n drawerTitle=\"How to Create a Custom Select Field: A Step-by-Step Guide\"\n/\u003e\n\nIf you want to learn more about custom components check out the [Admin \u003e Custom Component](../admin/components#field) docs.\n"])</script><script>self.__next_f.push([1,"55:T11eb,"])</script><script>self.__next_f.push([1,"\nThe Tabs Field is presentational-only and only affects the [Admin Panel](../admin/overview) (unless a tab is named). By using it, you can place fields within a nice layout component that separates certain sub-fields by a tabbed interface.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/tabs.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/tabs-dark.png\"\n alt=\"Shows a tabs field used to separate Hero and Page layout in the Payload Admin Panel\"\n caption=\"Tabs field type used to separate Hero fields from Page Layout\"\n/\u003e\n\nTo add a Tabs Field, set the `type` to `tabs` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyTabsField: Field = {\n // ...\n // highlight-start\n type: 'tabs',\n tabs: [\n // ...\n ]\n // highlight-end\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------ |\n| **`tabs`** \\* | Array of tabs to render within this Tabs field. |\n| **`admin`** | Admin-specific configuration. [More details](../admin/fields#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n\n### Tab-specific Config\n\nEach tab must have either a `name` or `label` and the required `fields` array. You can also optionally pass a `description` to render within each individual tab.\n\n| Option | Description |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **`name`** | Groups field data into an object when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | The label to render on the tab itself. Required when name is undefined, defaults to name converted to words. |\n| **`fields`** \\* | The fields to render within this tab. |\n| **`description`** | Optionally render a description within this tab to describe the contents of the tab itself. |\n| **`interfaceName`** | Create a top level, reusable [Typescript interface](../typescript/generating-types#custom-field-interfaces) \u0026 [GraphQL type](../graphql/graphql-schema#custom-field-schemas). (`name` must be present) |\n| **`virtual`** | Provide `true` to disable field in the database (`name` must be present). See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n type: 'tabs', // required\n tabs: [\n // required\n {\n label: 'Tab One Label', // required\n description: 'This will appear within the tab above the fields.',\n fields: [\n // required\n {\n name: 'someTextField',\n type: 'text',\n required: true,\n },\n ],\n },\n {\n name: 'tabTwo',\n label: 'Tab Two Label', // required\n interfaceName: 'TabTwo', // optional (`name` must be present)\n fields: [\n // required\n {\n name: 'numberField', // accessible via tabTwo.numberField\n type: 'number',\n required: true,\n },\n ],\n },\n ],\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"56:T1aa7,"])</script><script>self.__next_f.push([1,"\nThe Text Field is one of the most commonly used fields. It saves a string to the database and provides the [Admin Panel](../admin/overview) with a simple text input.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/text.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/text-dark.png\"\n alt=\"Shows a text field and read-only text field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Text field and read-only Text field\"\n/\u003e\n\nTo add a Text Field, set the `type` to `text` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyTextField: Field = {\n // ...\n type: 'text', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |\n| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`hasMany`** | Makes this field an ordered array of text instead of just a single text. |\n| **`minRows`** | Minimum number of texts in the array, if `hasMany` is set to true. |\n| **`maxRows`** | Maximum number of texts in the array, if `hasMany` is set to true. |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Text Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyTextField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Text Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Option | Description |\n| -------------- | ---------------------------------------------------------------------------------------------------------------- |\n| **`placeholder`** | Set this property to define a placeholder string in the text input. |\n| **`autoComplete`** | Set this property to a string that will be used for browser autocomplete. |\n| **`rtl`** | Override the default text direction of the Admin Panel for this field. Set to `true` to force right-to-left text direction. |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'pageTitle', // required\n type: 'text', // required\n required: true,\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"57:T1870,"])</script><script>self.__next_f.push([1,"\nThe Textarea Field is nearly identical to the [Text Field](./text) but it features a slightly larger input that is better suited to edit longer text.\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/fields/textarea.png\"\n srcDark=\"https://payloadcms.com/images/docs/fields/textarea-dark.png\"\n alt=\"Shows a textarea field and read-only textarea field in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot of a Textarea field and read-only Textarea field\"\n/\u003e\n\nTo add a Textarea Field, set the `type` to `textarea` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyTextareaField: Field = {\n // ...\n type: 'textarea', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`minLength`** | Used by the default validation function to ensure values are of a minimum character length. |\n| **`maxLength`** | Used by the default validation function to ensure values are of a maximum character length. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [More details](#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Admin Options\n\nThe customize the appearance and behavior of the Textarea Field in the [Admin Panel](../admin/overview), you can use the `admin` option:\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyTextareaField: Field = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\nThe Textarea Field inherits all of the default options from the base [Field Admin Config](../admin/fields#admin-options), plus the following additional options:\n\n| Option | Description |\n| -------------- | ---------------------------------------------------------------------------------------------------------------- |\n| **`placeholder`** | Set this property to define a placeholder string in the textarea. |\n| **`autoComplete`** | Set this property to a string that will be used for browser autocomplete. |\n| **`rtl`** | Override the default text direction of the Admin Panel for this field. Set to `true` to force right-to-left text direction. |\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'metaDescription', // required\n type: 'textarea', // required\n required: true,\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"58:Tb3a,"])</script><script>self.__next_f.push([1,"\nThe UI (user interface) Field gives you a ton of power to add your own React components directly into the [Admin Panel](../admin/overview), nested directly within your other fields. It has absolutely no effect on the data of your documents. It is presentational-only. Think of it as a way for you to \"plug in\" your own React components directly within your other fields, so you can provide your editors with new controls exactly where you want them to go.\n\nWith the UI Field, you can:\n\n- Add a custom message or block of text within the body of an Edit View to describe the purpose of surrounding fields\n- Add a \"Refund\" button to an Order's Edit View sidebar, which might make a fetch call to a custom `refund` endpoint\n- Add a \"view page\" button into a Pages List View to give editors a shortcut to view a page on the frontend of the site\n- Build a \"clear cache\" button or similar mechanism to manually clear caches of specific documents\n\nTo add a UI Field, set the `type` to `ui` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyUIField: Field = {\n // ...\n type: 'ui', // highlight-line\n}\n```\n\n## Config Options\n\n| Option | Description |\n| ------------------------------- | ------------------------------------------------------------------------------------------------------------------- |\n| **`name`** \\* | A unique identifier for this field. |\n| **`label`** | Human-readable label for this UI field. |\n| **`admin.components.Field`** \\* | React component to be rendered for this field within the Edit View. [More](../admin/components/#field) |\n| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](../admin/components/#field) |\n| **`admin.disableListColumn`** | Set `disableListColumn` to `true` to prevent the UI field from appearing in the list view column selector. |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'myCustomUIField', // required\n type: 'ui', // required\n admin: {\n components: {\n Field: '/path/to/MyCustomUIField',\n Cell: '/path/to/MyCustomUICell',\n },\n },\n },\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"59:T230d,"])</script><script>self.__next_f.push([1,"\nThe Upload Field allows for the selection of a Document from a Collection supporting [Uploads](../upload/overview), and\nformats the selection as a thumbnail in the Admin Panel.\n\nUpload fields are useful for a variety of use cases, such as:\n\n- To provide a `Page` with a featured image\n- To allow for a `Product` to deliver a downloadable asset like PDF or MP3\n- To give a layout building block the ability to feature a background image\n\n\u003cLightDarkImage\nsrcLight=\"https://payloadcms.com/images/docs/fields/upload.png\"\nsrcDark=\"https://payloadcms.com/images/docs/fields/upload-dark.png\"\nalt=\"Shows an upload field in the Payload Admin Panel\"\ncaption=\"Admin Panel screenshot of an Upload field\"\n/\u003e\n\nTo create an Upload Field, set the `type` to `upload` in your [Field Config](./overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const MyUploadField: Field = {\n // ...\n // highlight-start\n type: 'upload',\n relationTo: 'media',\n // highlight-end\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n To use the Upload Field, you must have a [Collection](../configuration/collections) configured to allow [Uploads](../upload/overview).\n\u003c/Banner\u003e\n\n## Config Options\n\n| Option | Description |\n|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`name`** \\* | To be used as the property name when stored and retrieved from the database. [More](../fields/overview#field-names) |\n| **`relationTo`** \\* | Provide a single collection `slug` to allow this field to accept a relation to. \u003cstrong\u003eNote: the related collection must be configured to support Uploads.\u003c/strong\u003e |\n| **`filterOptions`** | A query to filter which options appear in the UI and validate against. [More](#filtering-upload-options). |\n| **`hasMany`** | Boolean which, if set to true, allows this field to have many relations instead of only one. |\n| **`minRows`** | A number for the fewest allowed items during validation when a value is present. Used with hasMany. |\n| **`maxRows`** | A number for the most allowed items during validation when a value is present. Used with hasMany. |\n| **`maxDepth`** | Sets a number limit on iterations of related documents to populate when queried. [Depth](../queries/depth) |\n| **`label`** | Text used as a field label in the Admin Panel or an object with keys for each language. |\n| **`unique`** | Enforce that each entry in the Collection has a unique value for this field. |\n| **`validate`** | Provide a custom validation function that will be executed on both the Admin Panel and the backend. [More](../fields/overview#validation) |\n| **`index`** | Build an [index](../database/overview) for this field to produce faster queries. Set this field to `true` if your users will perform queries on this field's data often. |\n| **`saveToJWT`** | If this field is top-level and nested in a config supporting [Authentication](../authentication/overview), include its data in the user JWT. |\n| **`hooks`** | Provide Field Hooks to control logic for this field. [More details](../hooks/fields). |\n| **`access`** | Provide Field Access Control to denote what users can see and do with this field's data. [More details](../access-control/fields). |\n| **`hidden`** | Restrict this field's visibility from all APIs entirely. Will still be saved to the database, but will not appear in any API or the Admin Panel. |\n| **`defaultValue`** | Provide data to be used for this field's default value. [More](../fields/overview#default-values) |\n| **`displayPreview`** | Enable displaying preview of the uploaded file. Overrides related Collection's `displayPreview` option. [More](../upload/overview#collection-upload-options). |\n| **`localized`** | Enable localization for this field. Requires [localization to be enabled](../configuration/localization) in the Base config. |\n| **`required`** | Require this field to have a value. |\n| **`admin`** | Admin-specific configuration. [Admin Options](../admin/fields#admin-options). |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n| **`typescriptSchema`** | Override field type generation with providing a JSON schema |\n| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |\n\n_\\* An asterisk denotes that a property is required._\n\n## Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'backgroundImage', // required\n type: 'upload', // required\n relationTo: 'media', // required\n required: true,\n },\n ],\n}\n```\n\n## Filtering upload options\n\nOptions can be dynamically limited by supplying a [query constraint](../queries/overview), which will be used both\nfor validating input and filtering available uploads in the UI.\n\nThe `filterOptions` property can either be a `Where` query, or a function returning `true` to not filter, `false` to\nprevent all, or a `Where` query. When using a function, it will be\ncalled with an argument object with the following properties:\n\n| Property | Description |\n|---------------|-------------------------------------------------------------------------------------------------------|\n| `relationTo` | The collection `slug` to filter against, limited to this field's `relationTo` property |\n| `data` | An object containing the full collection or global document currently being edited |\n| `siblingData` | An object containing document data that is scoped to only fields within the same parent of this field |\n| `id` | The `id` of the current document being edited. `id` is `undefined` during the `create` operation |\n| `user` | An object containing the currently authenticated user |\n\n### Example#filter-options-example\n\n```ts\nconst uploadField = {\n name: 'image',\n type: 'upload',\n relationTo: 'media',\n filterOptions: {\n mimeType: { contains: 'image' },\n },\n}\n```\n\nYou can learn more about writing queries [here](../queries/overview).\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n When an upload field has both \u003cstrong\u003efilterOptions\u003c/strong\u003e and a custom{' '}\n \u003cstrong\u003evalidate\u003c/strong\u003e function, the api will not validate \u003cstrong\u003efilterOptions\u003c/strong\u003e{' '}\n unless you call the default upload field validation function imported from{' '}\n \u003cstrong\u003epayload/shared\u003c/strong\u003e in your validate function.\n\u003c/Banner\u003e\n\n## Bi-directional relationships\n\nThe `upload` field on its own is used to reference documents in an upload collection. This can be considered a \"one-way\"\nrelationship. If you wish to allow an editor to visit the upload document and see where it is being used, you may use\nthe `join` field in the upload enabled collection. Read more about bi-directional relationships using\nthe [Join field](./join)\n"])</script><script>self.__next_f.push([1,"5a:Tec6,"])</script><script>self.__next_f.push([1,"\n\u003cYouTube id=\"DoPLyXG26Dg\" title=\"Overview of Payload Access Control\" /\u003e\n\nAccess Control determines what a user can and cannot do with any given Document, as well as what they can and cannot see within the [Admin Panel](../admin/overview). By implementing Access Control, you can define granular restrictions based on the user, their roles (RBAC), Document data, or any other criteria your application requires.\n\nAccess Control functions are scoped to the _operation_, meaning you can have different rules for `create`, `read`, `update`, `delete`, etc. Access Control functions are executed _before_ any changes are made and _before_ any operations are completed. This allows you to determine if the user has the necessary permissions before fulfilling the request.\n\nThere are many use cases for Access Control, including:\n\n- Allowing anyone `read` access to all posts\n- Only allowing public access to posts where a `status` field is equal to `published`\n- Giving only users with a `role` field equal to `admin` the ability to delete posts\n- Allowing anyone to submit contact forms, but only logged in users to `read`, `update` or `delete` them\n- Restricting a user to only be able to see their own orders, but no-one else's\n- Allowing users that belong to a certain organization to access only that organization's resources\n\nThere are three main types of Access Control in Payload:\n\n- [Collection Access Control](./collections)\n- [Global Access Control](./globals)\n- [Field Access Control](./fields)\n\n## Default Access Control\n\nPayload provides default Access Control so that your data is secured behind [Authentication](../authentication) without additional configuration. To do this, Payload sets a default function that simply checks if a user is present on the request. You can override this default behavior by defining your own Access Control functions as needed.\n\nHere is the default Access Control that Payload provides:\n\n```ts\nconst defaultPayloadAccess = ({ req: { user } }) =\u003e {\n // Return `true` if a user is found\n // and `false` if it is undefined or null\n return Boolean(user) // highlight-line\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n In the [Local API](../local-api/overview), all Access Control is _skipped_ by default. This allows your server to have full control over your application. To opt back in, you can set the `overrideAccess` option to `false` in your requests.\n\u003c/Banner\u003e\n\n## The Access Operation\n\nThe Admin Panel responds dynamically to your changes to Access Control. For example, if you restrict editing `ExampleCollection` to only users that feature an \"admin\" role, Payload will **hide** that Collection from the Admin Panel entirely. This is super powerful and allows you to control who can do what within your Admin Panel using the same functions that secure your APIs.\n\nTo accomplish this, Payload exposes the [Access Operation](../authentication/operations#access). Upon login, Payload executes each Access Control function at the top level, across all Collections, Globals, and Fields, and returns a response that contains a reflection of what the currently authenticated user can do within your application.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n When your access control functions are executed via the [Access Operation](../authentication/operations#access), the `id` and `data` arguments will be `undefined`. This is because Payload is executing your functions without referencing a specific Document.\n\u003c/Banner\u003e\n\nIf you use `id` or `data` within your access control functions, make sure to check that they are defined first. If they are not, then you can assume that your Access Control is being executed via the Access Operation to determine solely what the user can do within the Admin Panel.\n"])</script><script>self.__next_f.push([1,"5b:T2faf,"])</script><script>self.__next_f.push([1,"\nCollection Access Control is [Access Control](../overview) used to restrict access to Documents within a [Collection](../collections/overview), as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Collection.\n\nTo add Access Control to a Collection, use the `access` property in your [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload';\n\nexport const CollectionWithAccessControl: CollectionConfig = {\n // ...\n access: { // highlight-line\n // ...\n },\n}\n```\n\n## Config Options\n\nAccess Control is specific to the operation of the request.\n\nTo add Access Control to a Collection, use the `access` property in your [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload';\n\nexport const CollectionWithAccessControl: CollectionConfig = {\n // ...\n // highlight-start\n access: {\n create: () =\u003e {...},\n read: () =\u003e {...},\n update: () =\u003e {...},\n delete: () =\u003e {...},\n\n // Auth-enabled Collections only\n admin: () =\u003e {...},\n unlock: () =\u003e {...},\n\n // Version-enabled Collections only\n readVersions: () =\u003e {...},\n },\n // highlight-end\n}\n```\n\nThe following options are available:\n\n| Function | Allows/Denies Access |\n| ----------------------- | -------------------------------------------- |\n| **`create`** | Used in the `create` operation. [More details](#create). |\n| **`read`** | Used in the `find` and `findByID` operations. [More details](#read). |\n| **`update`** | Used in the `update` operation. [More details](#update). |\n| **`delete`** | Used in the `delete` operation. [More details](#delete). |\n\nIf a Collection supports [`Authentication`](../authentication/overview), the following additional options are available:\n\n| Function | Allows/Denies Access |\n| ----------------------- | -------------------------------------------------------------- |\n| **`admin`** | Used to restrict access to the [Admin Panel](../admin/overview). [More details](#admin). |\n| **`unlock`** | Used to restrict which users can access the `unlock` operation. [More details](#unlock). |\n\nIf a Collection supports [Versions](../versions/overview), the following additional options are available:\n\n| Function | Allows/Denies Access |\n| ------------------ | ---------------------------------------------------------------------------------------------------------------------- |\n| **`readVersions`** | Used to control who can read versions, and who can't. Will automatically restrict the Admin UI version viewing access. [More details](#read-versions). |\n\n### Create\n\nReturns a boolean which allows/denies access to the `create` request.\n\nTo add create Access Control to a Collection, use the `create` property in the [Collection Config](../collections/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionWithCreateAccess: CollectionConfig = {\n // ...\n access: {\n // highlight-start\n create: ({ req: { user }, data }) =\u003e {\n return Boolean(user)\n },\n // highlight-end\n },\n}\n```\n\nThe following arguments are provided to the `create` function:\n\n| Option | Description |\n| ---------- | ---------------------------------------------------------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n| **`data`** | The data passed to create the document with. |\n\n### Read\n\nReturns a boolean which allows/denies access to the `read` request.\n\nTo add read Access Control to a Collection, use the `read` property in the [Collection Config](../collections/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionWithReadAccess: CollectionConfig = {\n // ...\n access: {\n // highlight-start\n read: ({ req: { user } }) =\u003e {\n return Boolean(user)\n },\n // highlight-end\n },\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n Return a [Query](../queries/overview) to limit the Documents to only those that match the constraint. This can be helpful to restrict users' access to specific Documents. [More details](../queries/overview).\n\u003c/Banner\u003e\n\nAs your application becomes more complex, you may want to define your function in a separate file and import them into your Collection Config:\n\n```ts\nimport type { Access } from 'payload'\n\nexport const canReadPage: Access = ({ req: { user } }) =\u003e {\n // Allow authenticated users\n if (user) {\n return true\n }\n\n // By returning a Query, guest users can read public Documents\n // Note: this assumes you have a `isPublic` checkbox field on your Collection\n return {\n isPublic: {\n equals: true,\n },\n }\n}\n```\n\nThe following arguments are provided to the `read` function:\n\n| Option | Description |\n| --------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n| **`id`** | `id` of document requested, if within `findByID`. |\n\n### Update\n\nReturns a boolean which allows/denies access to the `update` request.\n\nTo add update Access Control to a Collection, use the `update` property in the [Collection Config](../collections/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionWithUpdateAccess: CollectionConfig = {\n // ...\n access: {\n // highlight-start\n update: ({ req: { user }}) =\u003e {\n return Boolean(user)\n },\n // highlight-end\n },\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n Return a [Query](../queries/overview) to limit the Documents to only those that match the constraint. This can be helpful to restrict users' access to specific Documents. [More details](../queries/overview).\n\u003c/Banner\u003e\n\nAs your application becomes more complex, you may want to define your function in a separate file and import them into your Collection Config:\n\n```ts\nimport type { Access } from 'payload'\n\nexport const canUpdateUser: Access = ({ req: { user }, id }) =\u003e {\n // Allow users with a role of 'admin'\n if (user.roles \u0026\u0026 user.roles.some((role) =\u003e role === 'admin')) {\n return true\n }\n\n // allow any other users to update only oneself\n return user.id === id\n}\n```\n\nThe following arguments are provided to the `update` function:\n\n| Option | Description |\n| ---------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n| **`id`** | `id` of document requested to update. |\n| **`data`** | The data passed to update the document with. |\n\n### Delete\n\nSimilarly to the Update function, returns a boolean or a [query constraint](../queries/overview) to limit which documents can be deleted by which users.\n\nTo add delete Access Control to a Collection, use the `delete` property in the [Collection Config](../collections/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionWithDeleteAccess: CollectionConfig = {\n // ...\n access: {\n // highlight-start\n delete: ({ req: { user }}) =\u003e {\n return Boolean(user)\n },\n // highlight-end\n },\n}\n```\n\nAs your application becomes more complex, you may want to define your function in a separate file and import them into your Collection Config:\n\n```ts\nimport type { Access } from 'payload'\n\nexport const canDeleteCustomer: Access = async ({ req, id }) =\u003e {\n if (!id) {\n // allow the admin UI to show controls to delete since it is indeterminate without the `id`\n return true\n }\n\n // Query another Collection using the `id`\n const result = await req.payload.find({\n collection: 'contracts',\n limit: 0,\n depth: 0,\n where: {\n customer: { equals: id },\n },\n })\n\n return result.totalDocs === 0\n}\n```\n\nThe following arguments are provided to the `delete` function:\n\n| Option | Description |\n| --------- | --------------------------------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object with additional `user` property, which is the currently logged in user. |\n| **`id`** | `id` of document requested to delete.\n\n### Admin\n\nIf the Collection is use to access the [Admin Panel](../admin/overview#the-admin-user-collection), the `Admin` Access Control function determines whether or not the currently logged in user can access the admin UI.\n\nTo add Admin Access Control to a Collection, use the `admin` property in the [Collection Config](../collections/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionWithAdminAccess: CollectionConfig = {\n // ...\n access: {\n // highlight-start\n admin: ({ req: { user }}) =\u003e {\n return Boolean(user)\n },\n // highlight-end\n },\n}\n```\n\nThe following arguments are provided to the `admin` function:\n\n| Option | Description |\n| --------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n\n### Unlock\n\nDetermines which users can [unlock](../authentication/operations#unlock) other users who may be blocked from authenticating successfully due to [failing too many login attempts](../authentication/overview#options).\n\nTo add Unlock Access Control to a Collection, use the `unlock` property in the [Collection Config](../collections/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionWithUnlockAccess: CollectionConfig = {\n // ...\n access: {\n // highlight-start\n unlock: ({ req: { user }}) =\u003e {\n return Boolean(user)\n },\n // highlight-end\n },\n}\n```\n\nThe following arguments are provided to the `unlock` function:\n\n| Option | Description |\n| --------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n\n### Read Versions\n\nIf the Collection has [Versions](../versions/overview) enabled, the `readVersions` Access Control function determines whether or not the currently logged in user can access the version history of a Document.\n\nTo add Read Versions Access Control to a Collection, use the `readVersions` property in the [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionWithVersionsAccess: CollectionConfig = {\n // ...\n access: {\n // highlight-start\n readVersions: ({ req: { user }}) =\u003e {\n return Boolean(user)\n },\n // highlight-end\n },\n}\n```\n\nThe following arguments are provided to the `readVersions` function:\n\n| Option | Description |\n| --------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n"])</script><script>self.__next_f.push([1,"5c:T1338,"])</script><script>self.__next_f.push([1,"\nGlobal Access Control is [Access Control](../overview) used to restrict access to [Global](../globals/overview) Documents, as well as what they can and cannot see within the [Admin Panel](../admin/overview) as it relates to that Global.\n\nTo add Access Control to a Global, use the `access` property in your [Global Config](../configuration/globals):\n\n```ts\nimport type { GlobalConfig } from 'payload';\n\nexport const GlobalWithAccessControl: GlobalConfig = {\n // ...\n access: { // highlight-line\n // ...\n },\n}\n```\n\n## Config Options\n\nAccess Control is specific to the operation of the request.\n\nTo add Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../globals/overview):\n\n```ts\nimport { GlobalConfig } from 'payload'\n\nconst GlobalWithAccessControl: GlobalConfig = {\n // ...\n // highlight-start\n access: {\n read: ({ req: { user } }) =\u003e {...},\n update: ({ req: { user } }) =\u003e {...},\n\n // Version-enabled Globals only\n readVersion: () =\u003e {...},\n },\n // highlight-end\n}\n\nexport default Header\n```\n\nThe following options are available:\n\n| Function | Allows/Denies Access |\n| ----------------------- | -------------------------------------- |\n| **`read`** | Used in the `findOne` Global operation. [More details](#read). |\n| **`update`** | Used in the `update` Global operation. [More details](#update). |\n\nIf a Global supports [Versions](../versions/overview), the following additional options are available:\n\n| Function | Allows/Denies Access |\n| ------------------ | ---------------------------------------------------------------------------------------------------------------------- |\n| **`readVersions`** | Used to control who can read versions, and who can't. Will automatically restrict the Admin UI version viewing access. [More details](#read-versions). |\n\n### Read\n\nReturns a boolean result or optionally a [query constraint](../queries/overview) which limits who can read this global based on its current properties.\n\nTo add read Access Control to a [Global](../configuration/globals), use the `read` property in the [Global Config](../globals/overview):\n\n```ts\nimport { GlobalConfig } from 'payload'\n\nconst Header: GlobalConfig = {\n // ...\n // highlight-start\n read: {\n read: ({ req: { user } }) =\u003e {\n return Boolean(user)\n },\n }\n // highlight-end\n}\n```\n\nThe following arguments are provided to the `read` function:\n\n| Option | Description |\n| --------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n\n### Update\n\nReturns a boolean result or optionally a [query constraint](../queries/overview) which limits who can update this global based on its current properties.\n\nTo add update Access Control to a [Global](../configuration/globals), use the `access` property in the [Global Config](../globals/overview):\n\n```ts\nimport { GlobalConfig } from 'payload'\n\nconst Header: GlobalConfig = {\n // ...\n // highlight-start\n access: {\n update: ({ req: { user }, data }) =\u003e {\n return Boolean(user)\n },\n }\n // highlight-end\n}\n```\n\nThe following arguments are provided to the `update` function:\n\n| Option | Description |\n| ---------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n| **`data`** | The data passed to update the global with. |\n\n### Read Versions\n\nIf the Global has [Versions](../versions/overview) enabled, the `readVersions` Access Control function determines whether or not the currently logged in user can access the version history of a Document.\n\nTo add Read Versions Access Control to a Collection, use the `readVersions` property in the [Global Config](../globals/overview):\n\n```ts\nimport type { GlobalConfig } from 'payload'\n\nexport const GlobalWithVersionsAccess: GlobalConfig = {\n // ...\n access: {\n // highlight-start\n readVersions: ({ req: { user }}) =\u003e {\n return Boolean(user)\n },\n // highlight-end\n },\n}\n```\n\nThe following arguments are provided to the `readVersions` function:\n\n| Option | Description |\n| --------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user`. |\n"])</script><script>self.__next_f.push([1,"5d:T11b7,"])</script><script>self.__next_f.push([1,"\nField Access Control is [Access Control](../overview) used to restrict access to specific [Fields](../fields/overview) within a Document.\n\nTo add Access Control to a Field, use the `access` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { Field } from 'payload';\n\nexport const FieldWithAccessControl: Field = {\n // ...\n access: { // highlight-line\n // ...\n },\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n Field Access Controls does not support returning [Query](../queries/overview) constraints like [Collection Access Control](./collections) does.\n\u003c/Banner\u003e\n\n## Config Options\n\nAccess Control is specific to the operation of the request.\n\nTo add Access Control to a Field, use the `access` property in the [Field Config](../fields/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload';\n\nexport const Posts: CollectionConfig = {\n slug: 'posts',\n fields: [\n {\n name: 'title',\n type: 'text',\n // highlight-start\n access: {\n create: ({ req: { user } }) =\u003e { ... },\n read: ({ req: { user } }) =\u003e { ... },\n update: ({ req: { user } }) =\u003e { ... },\n },\n // highlight-end\n };\n ],\n};\n```\n\nThe following options are available:\n\n| Function | Purpose |\n| ----------------------- | -------------------------------------------------------------------------------- |\n| **`create`** | Allows or denies the ability to set a field's value when creating a new document. [More details](#create). |\n| **`read`** | Allows or denies the ability to read a field's value. [More details](#read). |\n| **`update`** | Allows or denies the ability to update a field's value [More details](#update). |\n\n### Create\n\nReturns a boolean which allows or denies the ability to set a field's value when creating a new document. If `false` is returned, any passed values will be discarded.\n\n**Available argument properties:**\n\n| Option | Description |\n| ----------------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |\n| **`data`** | The full data passed to create the document. |\n| **`siblingData`** | Immediately adjacent field data passed to create the document. |\n\n### Read\n\nReturns a boolean which allows or denies the ability to read a field's value. If `false`, the entire property is omitted from the resulting document.\n\n**Available argument properties:**\n\n| Option | Description |\n| ----------------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |\n| **`id`** | `id` of the document being read |\n| **`doc`** | The full document data. |\n| **`siblingData`** | Immediately adjacent field data of the document being read. |\n\n### Update\n\nReturns a boolean which allows or denies the ability to update a field's value. If `false` is returned, any passed values will be discarded.\n\nIf `false` is returned and you attempt to update the field's value, the operation will **not** throw an error however the field will be omitted from the update operation and the value will remain unchanged.\n\n**Available argument properties:**\n\n| Option | Description |\n| ----------------- | -------------------------------------------------------------------------- |\n| **`req`** | The [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object containing the currently authenticated `user` |\n| **`id`** | `id` of the document being updated |\n| **`data`** | The full data passed to update the document. |\n| **`siblingData`** | Immediately adjacent field data passed to update the document with. |\n| **`doc`** | The full document data, before the update is applied. |\n"])</script><script>self.__next_f.push([1,"5e:T15c4,"])</script><script>self.__next_f.push([1,"\nHooks allow you to execute your own side effects during specific events of the Document lifecycle. They allow you to do things like mutate data, perform business logic, integrate with third-parties, or anything else, all during precise moments within your application.\n\nWith Hooks, you can transform Payload from a traditional CMS into a fully-fledged application framework. There are many use cases for Hooks, including:\n\n- Modify data before it is read or updated\n- Encrypt and decrypt sensitive data\n- Integrate with a third-party CRM like HubSpot or Salesforce\n- Send a copy of uploaded files to Amazon S3 or similar\n- Process orders through a payment provider like Stripe\n- Send emails when contact forms are submitted\n- Track data ownership or changes over time\n\nThere are four main types of Hooks in Payload:\n\n- [Root Hooks](#root-hooks)\n- [Collection Hooks](../hooks/collections)\n- [Global Hooks](../hooks/globals)\n- [Field Hooks](../hooks/fields)\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n Payload also ships a set of _React_ hooks that you can use in your frontend application. Although they share a common name, these are very different things and should not be confused. [More details](../admin/hooks).\n\u003c/Banner\u003e\n\n## Root Hooks\n\nRoot Hooks are not associated with any specific Collection, Global, or Field. They are useful for globally-oriented side effects, such as when an error occurs at the application level.\n\nTo add Root Hooks, use the `hooks` property in your [Payload Config](../configuration/config):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n // highlight-start\n hooks: {\n afterError:[() =\u003e {...}]\n },\n // highlight-end\n})\n```\n\nThe following options are available:\n\n| Option | Description |\n|--------------|-----------------------------------------------------------------------------------------------|\n| **`afterError`** | Runs after an error occurs in the Payload application. |\n\n### afterError\n\nThe `afterError` Hook is triggered when an error occurs in the Payload application. This can be useful for logging errors to a third-party service, sending an email to the development team, logging the error to Sentry or DataDog, etc. The output can be used to transform the result object / status code.\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n hooks: {\n afterError: [async ({ error }) =\u003e {\n // Do something\n }]\n },\n})\n```\n\nThe following arguments are provided to the `afterError` Hook:\n\n| Argument | Description |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`error`** | The error that occurred. |\n| **`context`** | Custom context passed between Hooks. [More details](./context). |\n| **`graphqlResult`** | The GraphQL result object, available if the hook is executed within a GraphQL context. |\n| **`req`** | The `PayloadRequest` object that extends [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request). Contains currently authenticated `user` and the Local API instance `payload`. |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. This will be `undefined` if the hook is executed from a non-collection endpoint or GraphQL. |\n| **`result`** | The formatted error result object, available if the hook is executed from a REST context. |\n## Async vs. Synchronous\n\nAll Hooks can be written as either synchronous or asynchronous functions. Choosing the right type depends on your use case, but switching between the two is as simple as adding or removing the `async` keyword.\n\n#### Asynchronous\n\nIf the Hook should modify data before a Document is updated or created, and it relies on asynchronous actions such as fetching data from a third party, it might make sense to define your Hook as an asynchronous function. This way you can be sure that your Hook completes before the operation's lifecycle continues. Async hooks are run in series - so if you have two async hooks defined, the second hook will wait for the first to complete before it starts.\n\n#### Synchronous\n\nIf your Hook simply performs a side-effect, such as updating a CRM, it might be okay to define it synchronously, so the Payload operation does not have to wait for your hook to complete.\n\n## Server-only Execution\n\nHooks are only triggered on the server and are automatically excluded from the client-side bundle. This means that you can safely use sensitive business logic in your Hooks without worrying about exposing it to the client.\n"])</script><script>self.__next_f.push([1,"5f:T8856,"])</script><script>self.__next_f.push([1,"\nCollection Hooks are [Hooks](./overview) that run on Documents within a specific [Collection](../configuration/collections). They allow you to execute your own logic during specific events of the Document lifecycle.\n\nTo add Hooks to a Collection, use the `hooks` property in your [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload';\n\nexport const CollectionWithHooks: CollectionConfig = {\n // ...\n hooks: { // highlight-line\n // ...\n },\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n You can also set hooks on the field-level to isolate hook logic to specific fields. [More details](./fields).\n\u003c/Banner\u003e\n\n## Config Options\n\nAll Collection Hooks accept an array of [synchronous or asynchronous functions](./overview#async-vs-synchronous). Each Collection Hook receives specific arguments based on its own type, and has the ability to modify specific outputs.\n\n```ts\nimport type { CollectionConfig } from 'payload';\n\nexport const CollectionWithHooks: CollectionConfig = {\n // ...\n // highlight-start\n hooks: {\n beforeOperation: [(args) =\u003e {...}],\n beforeValidate: [(args) =\u003e {...}],\n beforeDelete: [(args) =\u003e {...}],\n beforeChange: [(args) =\u003e {...}],\n beforeRead: [(args) =\u003e {...}],\n afterChange: [(args) =\u003e {...}],\n afterRead: [(args) =\u003e {...}],\n afterDelete: [(args) =\u003e {...}],\n afterOperation: [(args) =\u003e {...}],\n afterError: [(args) =\u003e {....}],\n\n // Auth-enabled Hooks\n beforeLogin: [(args) =\u003e {...}],\n afterLogin: [(args) =\u003e {...}],\n afterLogout: [(args) =\u003e {...}],\n afterRefresh: [(args) =\u003e {...}],\n afterMe: [(args) =\u003e {...}],\n afterForgotPassword: [(args) =\u003e {...}],\n refresh: [(args) =\u003e {...}],\n me: [(args) =\u003e {...}],\n },\n // highlight-end\n}\n```\n\n### beforeOperation\n\nThe `beforeOperation` hook can be used to modify the arguments that operations accept or execute side-effects that run before an operation begins.\n\nAvailable Collection operations include `create`, `read`, `update`, `delete`, `login`, `refresh`, and `forgotPassword`.\n\n```ts\nimport type { CollectionBeforeOperationHook } from 'payload'\n\nconst beforeOperationHook: CollectionBeforeOperationHook = async ({\n args,\n operation,\n req,\n}) =\u003e {\n return args // return modified operation arguments as necessary\n}\n```\n\nThe following arguments are provided to the `beforeOperation` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between Hooks. [More details](./context). |\n| **`operation`** | The name of the operation that this hook is running within. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### beforeValidate\n\nRuns before the `create` and `update` operations. This hook allows you to add or format data before the incoming data is validated server-side.\n\nPlease do note that this does not run before the client-side validation. If you added a `validate` function, this would be the lifecycle:\n\n1. `validate` runs on the client\n2. if successful, `beforeValidate` runs on the server\n3. `validate` runs on the server\n\n```ts\nimport type { CollectionBeforeValidateHook } from 'payload'\n\nconst beforeValidateHook: CollectionBeforeValidateHook = async ({\n data,\n}) =\u003e {\n return data\n}\n```\n\nThe following arguments are provided to the `beforeValidate` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between Hooks. [More details](./context). |\n| **`data`** | The incoming data passed through the operation. |\n| **`operation`** | The name of the operation that this hook is running within. |\n| **`originalDoc`** | The Document before changes are applied. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### beforeChange\n\nImmediately following validation, `beforeChange` hooks will run within `create` and `update` operations. At this stage, you can be confident that the data that will be saved to the document is valid in accordance to your field validations. You can optionally modify the shape of data to be saved.\n\n```ts\nimport type { CollectionBeforeChangeHook } from 'payload'\n\nconst beforeChangeHook: CollectionBeforeChangeHook = async ({\n data,\n}) =\u003e {\n return data\n}\n```\n\nThe following arguments are provided to the `beforeChange` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`data`** | The incoming data passed through the operation. |\n| **`operation`** | The name of the operation that this hook is running within. |\n| **`originalDoc`** | The Document before changes are applied. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### afterChange\n\nAfter a document is created or updated, the `afterChange` hook runs. This hook is helpful to recalculate statistics such as total sales within a global, syncing user profile changes to a CRM, and more.\n\n```ts\nimport type { CollectionAfterChangeHook } from 'payload'\n\nconst afterChangeHook: CollectionAfterChangeHook = async ({\n doc,\n}) =\u003e {\n return doc\n}\n```\n\nThe following arguments are provided to the `afterChange` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`doc`** | The resulting Document after changes are applied. |\n| **`operation`** | The name of the operation that this hook is running within. |\n| **`previousDoc`** | The Document before changes were applied. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### beforeRead\n\nRuns before `find` and `findByID` operations are transformed for output by `afterRead`. This hook fires before hidden fields are removed and before localized fields are flattened into the requested locale. Using this Hook will provide you with all locales and all hidden fields via the `doc` argument.\n\n```ts\nimport type { CollectionBeforeReadHook } from 'payload'\n\nconst beforeReadHook: CollectionBeforeReadHook = async ({\n doc,\n}) =\u003e {\n return doc\n}\n```\n\nThe following arguments are provided to the `beforeRead` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`doc`** | The resulting Document after changes are applied. |\n| **`query`** | The [Query](../queries/overview) of the request.\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### afterRead\n\nRuns as the last step before documents are returned. Flattens locales, hides protected fields, and removes fields that users do not have access to.\n\n```ts\nimport type { CollectionAfterReadHook } from 'payload'\n\nconst afterReadHook: CollectionAfterReadHook = async ({\n doc,\n}) =\u003e {\n return doc\n}\n```\n\nThe following arguments are provided to the `afterRead` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`doc`** | The resulting Document after changes are applied. |\n| **`query`** | The [Query](../queries/overview) of the request.\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### beforeDelete\n\nRuns before the `delete` operation. Returned values are discarded.\n\n```ts\nimport type { CollectionBeforeDeleteHook } from 'payload';\n\nconst beforeDeleteHook: CollectionBeforeDeleteHook = async ({\n req,\n id,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `beforeDelete` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`id`** | The ID of the Document being deleted. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### afterDelete\n\nRuns immediately after the `delete` operation removes records from the database. Returned values are discarded.\n\n```ts\nimport type { CollectionAfterDeleteHook } from 'payload';\n\nconst afterDeleteHook: CollectionAfterDeleteHook = async ({\n req,\n id,\n doc,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `afterDelete` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`doc`** | The resulting Document after changes are applied. |\n| **`id`** | The ID of the Document that was deleted. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### afterOperation\n\nThe `afterOperation` hook can be used to modify the result of operations or execute side-effects that run after an operation has completed.\n\nAvailable Collection operations include `create`, `find`, `findByID`, `update`, `updateByID`, `delete`, `deleteByID`, `login`, `refresh`, and `forgotPassword`.\n\n```ts\nimport type { CollectionAfterOperationHook } from 'payload'\n\nconst afterOperationHook: CollectionAfterOperationHook = async ({\n result,\n}) =\u003e {\n return result\n}\n```\n\nThe following arguments are provided to the `afterOperation` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`args`** | The arguments passed into the operation. |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n| **`operation`** | The name of the operation that this hook is running within. |\n| **`result`** | The result of the operation, before modifications. |\n\n### afterError\n\nThe `afterError` Hook is triggered when an error occurs in the Payload application. This can be useful for logging errors to a third-party service, sending an email to the development team, logging the error to Sentry or DataDog, etc. The output can be used to transform the result object / status code.\n\n```ts\nimport type { CollectionAfterErrorHook } from 'payload';\n\nconst afterDeleteHook: CollectionAfterErrorHook = async ({\n req,\n id,\n doc,\n}) =\u003e {...}\n```\nThe following arguments are provided to the `afterError` Hook:\n\n| Argument | Description |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`error`** | The error that occurred. |\n| **`context`** | Custom context passed between Hooks. [More details](./context). |\n| **`graphqlResult`** | The GraphQL result object, available if the hook is executed within a GraphQL context. |\n| **`req`** | The `PayloadRequest` object that extends [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request). Contains currently authenticated `user` and the Local API instance `payload`. |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`result`** | The formatted error result object, available if the hook is executed from a REST context. |\n\n### beforeLogin\n\nFor [Auth-enabled Collections](../authentication/overview), this hook runs during `login` operations where a user with the provided credentials exist, but before a token is generated and added to the response. You can optionally modify the user that is returned, or throw an error in order to deny the login operation.\n\n```ts\nimport type { CollectionBeforeLoginHook } from 'payload'\n\nconst beforeLoginHook: CollectionBeforeLoginHook = async ({\n user,\n}) =\u003e {\n return user\n}\n```\n\nThe following arguments are provided to the `beforeLogin` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n| **`user`** | The user being logged in. |\n\n### afterLogin\n\nFor [Auth-enabled Collections](../authentication/overview), this hook runs after successful `login` operations. You can optionally modify the user that is returned.\n\n```ts\nimport type { CollectionAfterLoginHook } from 'payload';\n\nconst afterLoginHook: CollectionAfterLoginHook = async ({\n user,\n token,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `afterLogin` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n| **`token`** | The token generated for the user. |\n| **`user`** | The user being logged in. |\n\n### afterLogout\n\nFor [Auth-enabled Collections](../authentication/overview), this hook runs after `logout` operations.\n\n```ts\nimport type { CollectionAfterLogoutHook } from 'payload';\n\nconst afterLogoutHook: CollectionAfterLogoutHook = async ({\n req,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `afterLogout` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### afterMe\n\nFor [Auth-enabled Collections](../authentication/overview), this hook runs after `me` operations.\n\n```ts\nimport type { CollectionAfterMeHook } from 'payload';\n\nconst afterMeHook: CollectionAfterMeHook = async ({\n req,\n response,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `afterMe` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n| **`response`** | The response to return. |\n\n### afterRefresh\n\nFor [Auth-enabled Collections](../authentication/overview), this hook runs after `refresh` operations.\n\n```ts\nimport type { CollectionAfterRefreshHook } from 'payload';\n\nconst afterRefreshHook: CollectionAfterRefreshHook = async ({\n token,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `afterRefresh` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`exp`** | The expiration time of the token. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n| **`token`** | The newly refreshed user token. |\n\n### afterForgotPassword\n\nFor [Auth-enabled Collections](../authentication/overview), this hook runs after successful `forgotPassword` operations. Returned values are discarded.\n\n```ts\nimport type { CollectionAfterForgotPasswordHook } from 'payload'\n\nconst afterForgotPasswordHook: CollectionAfterForgotPasswordHook = async ({\n args,\n context,\n collection,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `afterForgotPassword` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`args`** | The arguments passed into the operation. |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n\n### refresh\n\nFor [Auth-enabled Collections](../authentication/overview), this hook allows you to optionally replace the default behavior of the `refresh` operation with your own. If you optionally return a value from your hook, the operation will not perform its own logic and continue.\n\n```ts\nimport type { CollectionRefreshHook } from 'payload'\n\nconst myRefreshHook: CollectionRefreshHook = async ({\n args,\n user,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `afterRefresh` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`args`** | The arguments passed into the operation. |\n| **`user`** | The user being logged in. |\n\n### me\n\nFor [Auth-enabled Collections](../authentication/overview), this hook allows you to optionally replace the default behavior of the `me` operation with your own. If you optionally return a value from your hook, the operation will not perform its own logic and continue.\n\n```ts\nimport type { CollectionMeHook } from 'payload'\n\nconst meHook: CollectionMeHook = async ({\n args,\n user,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `me` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`args`** | The arguments passed into the operation. |\n| **`user`** | The user being logged in. |\n\n## TypeScript\n\nPayload exports a type for each Collection hook which can be accessed as follows:\n\n```ts\nimport type {\n CollectionBeforeOperationHook,\n CollectionBeforeValidateHook,\n CollectionBeforeChangeHook,\n CollectionAfterChangeHook,\n CollectionAfterReadHook,\n CollectionBeforeReadHook,\n CollectionBeforeDeleteHook,\n CollectionAfterDeleteHook,\n CollectionBeforeLoginHook,\n CollectionAfterLoginHook,\n CollectionAfterLogoutHook,\n CollectionAfterRefreshHook,\n CollectionAfterMeHook,\n CollectionAfterForgotPasswordHook,\n CollectionRefreshHook,\n CollectionMeHook,\n} from 'payload'\n```\n"])</script><script>self.__next_f.push([1,"60:T2c1a,"])</script><script>self.__next_f.push([1,"\nGlobal Hooks are [Hooks](./overview) that run on [Global](../configuration/globals) Documents. They allow you to execute your own logic during specific events of the Document lifecycle.\n\nTo add Hooks to a Global, use the `hooks` property in your [Global Config](../configuration/globals):\n\n```ts\nimport type { GlobalConfig } from 'payload';\n\nexport const GlobalWithHooks: GlobalConfig = {\n // ...\n hooks: { // highlight-line\n // ...\n },\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n You can also set hooks on the field-level to isolate hook logic to specific fields. [More details](./fields).\n\u003c/Banner\u003e\n\n## Config Options\n\nAll Global Hooks accept an array of [synchronous or asynchronous functions](./overview#async-vs-synchronous). Each Global Hook receives specific arguments based on its own type, and has the ability to modify specific outputs.\n\n```ts\nimport type { GlobalConfig } from 'payload';\n\nconst GlobalWithHooks: GlobalConfig = {\n // ...\n // highlight-start\n hooks: {\n beforeValidate: [(args) =\u003e {...}],\n beforeChange: [(args) =\u003e {...}],\n beforeRead: [(args) =\u003e {...}],\n afterChange: [(args) =\u003e {...}],\n afterRead: [(args) =\u003e {...}],\n }\n // highlight-end\n}\n```\n\n### beforeValidate\n\nRuns before the `update` operation. This hook allows you to add or format data before the incoming data is validated.\n\n```ts\nimport type { GlobalBeforeValidateHook } from 'payload'\n\nconst beforeValidateHook: GlobalBeforeValidateHook = async ({\n data,\n req,\n originalDoc,\n}) =\u003e {\n return data\n}\n```\n\nThe following arguments are provided to the `beforeValidate` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`global`** | The [Global](../configuration/globals) in which this Hook is running against. |\n| **`context`** | Custom context passed between Hooks. [More details](./context). |\n| **`data`** | The incoming data passed through the operation. |\n| **`originalDoc`** | The Document before changes are applied. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### beforeChange\n\nImmediately following validation, `beforeChange` hooks will run within the `update` operation. At this stage, you can be confident that the data that will be saved to the document is valid in accordance to your field validations. You can optionally modify the shape of data to be saved.\n\n```ts\nimport type { GlobalBeforeChangeHook } from 'payload'\n\nconst beforeChangeHook: GlobalBeforeChangeHook = async ({\n data,\n req,\n originalDoc,\n}) =\u003e {\n return data\n}\n```\n\nThe following arguments are provided to the `beforeChange` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`global`** | The [Global](../configuration/globals) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`data`** | The incoming data passed through the operation. |\n| **`originalDoc`** | The Document before changes are applied. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### afterChange\n\nAfter a global is updated, the `afterChange` hook runs. Use this hook to purge caches of your applications, sync site data to CRMs, and more.\n\n```ts\nimport type { GlobalAfterChangeHook } from 'payload'\n\nconst afterChangeHook: GlobalAfterChangeHook = async ({\n doc,\n previousDoc,\n req,\n}) =\u003e {\n return data\n}\n```\n\nThe following arguments are provided to the `afterChange` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`global`** | The [Global](../configuration/globals) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`doc`** | The resulting Document after changes are applied. |\n| **`previousDoc`** | The Document before changes were applied. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### beforeRead\n\nRuns before `findOne` global operation is transformed for output by `afterRead`. This hook fires before hidden fields are removed and before localized fields are flattened into the requested locale. Using this Hook will provide you with all locales and all hidden fields via the `doc` argument.\n\n```ts\nimport type { GlobalBeforeReadHook } from 'payload'\n\nconst beforeReadHook: GlobalBeforeReadHook = async ({\n doc,\n req,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `beforeRead` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`global`** | The [Global](../configuration/globals) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`doc`** | The resulting Document after changes are applied. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n### afterRead\n\nRuns as the last step before a global is returned. Flattens locales, hides protected fields, and removes fields that users do not have access to.\n\n```ts\nimport type { GlobalAfterReadHook } from 'payload'\n\nconst afterReadHook: GlobalAfterReadHook = async ({\n doc,\n req,\n findMany,\n}) =\u003e {...}\n```\n\nThe following arguments are provided to the `beforeRead` hook:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`global`** | The [Global](../configuration/globals) in which this Hook is running against. |\n| **`context`** | Custom context passed between hooks. [More details](./context). |\n| **`findMany`** | Boolean to denote if this hook is running against finding one, or finding many (useful in versions). |\n| **`doc`** | The resulting Document after changes are applied. |\n| **`query`** | The [Query](../queries/overview) of the request.\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n\n## TypeScript\n\nPayload exports a type for each Global hook which can be accessed as follows:\n\n```ts\nimport type {\n GlobalBeforeValidateHook,\n GlobalBeforeChangeHook,\n GlobalAfterChangeHook,\n GlobalBeforeReadHook,\n GlobalAfterReadHook,\n} from 'payload'\n```\n"])</script><script>self.__next_f.push([1,"61:T30ff,"])</script><script>self.__next_f.push([1,"\nField Hooks are [Hooks](./overview) that run on Documents on a per-field basis. They allow you to execute your own logic during specific events of the Document lifecycle. Field Hooks offer incredible potential for isolating your logic from the rest of your [Collection Hooks](./collections) and [Global Hooks](./globals).\n\nTo add Hooks to a Field, use the `hooks` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { Field } from 'payload';\n\nexport const FieldWithHooks: Field = {\n // ...\n hooks: { // highlight-line\n // ...\n },\n}\n```\n\n## Config Options\n\nAll Field Hooks accept an array of synchronous or asynchronous functions. These functions can optionally modify the return value of the field before the operation continues. All Field Hooks are formatted to accept the same arguments, although some arguments may be `undefined` based the specific hook type.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n Due to GraphQL's typed nature, changing the type of data that you return from a field will produce errors in the [GraphQL API](../graphql/overview). If you need to change the shape or type of data, consider [Collection Hooks](./collections) or [Global Hooks](./hooks) instead.\n\u003c/Banner\u003e\n\nTo add hooks to a Field, use the `hooks` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { Field } from 'payload';\n\nconst FieldWithHooks: Field = {\n name: 'name',\n type: 'text',\n // highlight-start\n hooks: {\n beforeValidate: [(args) =\u003e {...}],\n beforeChange: [(args) =\u003e {...}],\n beforeDuplicate: [(args) =\u003e {...}],\n afterChange: [(args) =\u003e {...}],\n afterRead: [(args) =\u003e {...}],\n }\n // highlight-end\n}\n```\n\nThe following arguments are provided to all Field Hooks:\n\n| Option | Description |\n| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`collection`** | The [Collection](../configuration/collections) in which this Hook is running against. If the field belongs to a Global, this will be `null`. |\n| **`context`** | Custom context passed between Hooks. [More details](./context). |\n| **`data`** | In the `afterRead` hook this is the full Document. In the `create` and `update` operations, this is the incoming data passed through the operation. |\n| **`field`** | The [Field](../fields/overview) which the Hook is running against. |\n| **`findMany`** | Boolean to denote if this hook is running against finding one, or finding many within the `afterRead` hook. |\n| **`global`** | The [Global](../configuration/globals) in which this Hook is running against. If the field belongs to a Collection, this will be `null`. |\n| **`operation`** | The name of the operation that this hook is running within. Useful within `beforeValidate`, `beforeChange`, and `afterChange` hooks to differentiate between `create` and `update` operations. |\n| **`originalDoc`** | In the `update` operation, this is the Document before changes were applied. In the `afterChange` hook, this is the resulting Document. |\n| **`overrideAccess`** | A boolean to denote if the current operation is overriding [Access Control](../access-control/overview). |\n| **`path`** | The path to the [Field](../fields/overview) in the schema. |\n| **`previousDoc`** | In the `afterChange` Hook, this is the Document before changes were applied. |\n| **`previousSiblingDoc`** | The sibling data of the Document before changes being applied, only in `beforeChange` and `afterChange` hook. |\n| **`previousValue`** | The previous value of the field, before changes, only in `beforeChange` and `afterChange` hooks. |\n| **`req`** | The [Web Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object. This is mocked for [Local API](../local-api/overview) operations. |\n| **`schemaPath`** | The path of the [Field](../fields/overview) in the schema. |\n| **`siblingData`** | The data of sibling fields adjacent to the field that the Hook is running against. |\n| **`siblingDocWithLocales`** | The sibling data of the Document with all [Locales](../configuration/localization). |\n| **`value`** | The value of the [Field](../fields/overview). |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n It's a good idea to conditionally scope your logic based on which operation is executing. For example, if you are writing a `beforeChange` hook, you may want to perform different logic based on if the current `operation` is `create` or `update`.\n\u003c/Banner\u003e\n\n### beforeValidate\n\nRuns before the `update` operation. This hook allows you to pre-process or format field data before it undergoes validation.\n\n```ts\nimport type { Field } from 'payload'\n\nconst usernameField: Field = {\n name: 'username',\n type: 'text',\n hooks: {\n beforeValidate: [\n ({ value }) =\u003e {\n // Trim whitespace and convert to lowercase\n return value.trim().toLowerCase()\n },\n ],\n },\n}\n```\n\nIn this example, the `beforeValidate` hook is used to process the `username` field. The hook takes the incoming value of\nthe field and transforms it by trimming whitespace and converting it to lowercase. This ensures that the username is\nstored in a consistent format in the database.\n\n### beforeChange\n\nImmediately following validation, `beforeChange` hooks will run within `create` and `update` operations. At this stage,\nyou can be confident that the field data that will be saved to the document is valid in accordance to your field\nvalidations.\n\n```ts\nimport type { Field } from 'payload'\n\nconst emailField: Field = {\n name: 'email',\n type: 'email',\n hooks: {\n beforeChange: [\n ({ value, operation }) =\u003e {\n if (operation === 'create') {\n // Perform additional validation or transformation for 'create' operation\n }\n return value\n },\n ],\n },\n}\n```\n\nIn the `emailField`, the `beforeChange` hook checks the `operation` type. If the operation is `create`, it performs\nadditional validation or transformation on the email field value. This allows for operation-specific logic to be applied\nto the field.\n\n### afterChange\n\nThe `afterChange` hook is executed after a field's value has been changed and saved in the database. This hook is useful\nfor post-processing or triggering side effects based on the new value of the field.\n\n```ts\nimport type { Field } from 'payload'\n\nconst membershipStatusField: Field = {\n name: 'membershipStatus',\n type: 'select',\n options: [\n { label: 'Standard', value: 'standard' },\n { label: 'Premium', value: 'premium' },\n { label: 'VIP', value: 'vip' },\n ],\n hooks: {\n afterChange: [\n ({ value, previousValue, req }) =\u003e {\n if (value !== previousValue) {\n // Log or perform an action when the membership status changes\n console.log(\n `User ID ${req.user.id} changed their membership status from ${previousValue} to ${value}.`,\n )\n // Here, you can implement actions that could track conversions from one tier to another\n }\n },\n ],\n },\n}\n```\n\nIn this example, the `afterChange` hook is used with a `membershipStatusField`, which allows users to select their\nmembership level (Standard, Premium, VIP). The hook monitors changes in the membership status. When a change occurs, it\nlogs the update and can be used to trigger further actions, such as tracking conversion from one tier to another or\nnotifying them about changes in their membership benefits.\n\n### afterRead\n\nThe `afterRead` hook is invoked after a field value is read from the database. This is ideal for formatting or\ntransforming the field data for output.\n\n```ts\nimport type { Field } from 'payload'\n\nconst dateField: Field = {\n name: 'createdAt',\n type: 'date',\n hooks: {\n afterRead: [\n ({ value }) =\u003e {\n // Format date for display\n return new Date(value).toLocaleDateString()\n },\n ],\n },\n}\n```\n\nHere, the `afterRead` hook for the `dateField` is used to format the date into a more readable format\nusing `toLocaleDateString()`. This hook modifies the way the date is presented to the user, making it more\nuser-friendly.\n\n### beforeDuplicate\n\nThe `beforeDuplicate` field hook is called on each locale (when using localization), when duplicating a document. It may be used when documents having the\nexact same properties may cause issue. This gives you a way to avoid duplicate names on `unique`, `required` fields or when external systems expect non-repeating values on documents.\n\nThis hook gets called before the `beforeValidate` and `beforeChange` hooks are called.\n\nBy Default, unique and required text fields Payload will append \"- Copy\" to the original document value. The default is not added if your field has its own, you must return non-unique values from your beforeDuplicate hook to avoid errors or enable the `disableDuplicate` option on the collection.\nHere is an example of a number field with a hook that increments the number to avoid unique constraint errors when duplicating a document:\n\n```ts\nimport type { Field } from 'payload'\n\nconst numberField: Field = {\n name: 'number',\n type: 'number',\n hooks: {\n // increment existing value by 1\n beforeDuplicate: [({ value }) =\u003e {\n return (value ?? 0) + 1\n }],\n }\n}\n```\n\n## TypeScript\n\nPayload exports a type for field hooks which can be accessed and used as follows:\n\n```ts\nimport type { FieldHook } from 'payload'\n\n// Field hook type is a generic that takes three arguments:\n// 1: The document type\n// 2: The value type\n// 3: The sibling data type\n\ntype ExampleFieldHook = FieldHook\u003cExampleDocumentType, string, SiblingDataType\u003e\n\nconst exampleFieldHook: ExampleFieldHook = (args) =\u003e {\n const {\n value, // Typed as `string` as shown above\n data, // Typed as a Partial of your ExampleDocumentType\n siblingData, // Typed as a Partial of SiblingDataType\n originalDoc, // Typed as ExampleDocumentType\n operation,\n req,\n } = args\n\n // Do something here...\n\n return value // should return a string as typed above, undefined, or null\n}\n```\n"])</script><script>self.__next_f.push([1,"62:T1251,"])</script><script>self.__next_f.push([1,"\nThe `context` object is used to share data across different Hooks. This persists throughout the entire lifecycle of a request and is available within every Hook. By setting properties to `req.context`, you can effectively logic across multiple Hooks.\n\n## When To Use Context\n\nContext gives you a way forward on otherwise difficult problems such as:\n\n1. **Passing data between Hooks**: Needing data in multiple Hooks from a 3rd party API, it could be retrieved and used in `beforeChange` and later used again in an `afterChange` hook without having to fetch it twice.\n2. **Preventing infinite loops**: Calling `payload.update()` on the same document that triggered an `afterChange` hook will create an infinite loop, control the flow by assigning a no-op condition to context\n3. **Passing data to local API**: Setting values on the `req.context` and pass it to `payload.create()` you can provide additional data to hooks without adding extraneous fields.\n4. **Passing data between hooks and middleware or custom endpoints**: Hooks could set context across multiple collections and then be used in a final `postMiddleware`.\n\n## How To Use Context\n\nLet's see examples on how context can be used in the first two scenarios mentioned above:\n\n### Passing Data Between Hooks\n\nTo pass data between hooks, you can assign values to context in an earlier hook in the lifecycle of a request and expect it the context in a later hook.\n\nFor example:\n\n```ts\nconst Customer: CollectionConfig = {\n slug: 'customers',\n hooks: {\n beforeChange: [\n async ({ context, data }) =\u003e {\n // assign the customerData to context for use later\n context.customerData = await fetchCustomerData(data.customerID)\n return {\n ...data,\n // some data we use here\n name: context.customerData.name,\n }\n },\n ],\n afterChange: [\n\n async ({ context, doc, req }) =\u003e {\n // use context.customerData without needing to fetch it again\n if (context.customerData.contacted === false) {\n createTodo('Call Customer', context.customerData)\n }\n },\n ],\n },\n fields: [\n /* ... */\n ],\n}\n```\n\n### Preventing Infinite Loops\n\nLet's say you have an `afterChange` hook, and you want to do a calculation inside the hook (as the document ID needed for the calculation is available in the `afterChange` hook, but not in the `beforeChange` hook). Once that's done, you want to update the document with the result of the calculation.\n\nBad example:\n\n```ts\nconst Customer: CollectionConfig = {\n slug: 'customers',\n hooks: {\n afterChange: [\n async ({ doc }) =\u003e {\n await payload.update({\n // DANGER: updating the same slug as the collection in an afterChange will create an infinite loop!\n collection: 'customers',\n id: doc.id,\n data: {\n ...(await fetchCustomerData(data.customerID)),\n },\n })\n },\n ],\n },\n fields: [\n /* ... */\n ],\n}\n```\n\nInstead of the above, we need to tell the `afterChange` hook to not run again if it performs the update (and thus not update itself again). We can solve that with context.\n\nFixed example:\n\n```ts\nconst MyCollection: CollectionConfig = {\n slug: 'slug',\n hooks: {\n afterChange: [\n async ({ context, doc }) =\u003e {\n // return if flag was previously set\n if (context.triggerAfterChange === false) {\n return\n }\n await payload.update({\n collection: contextHooksSlug,\n id: doc.id,\n data: {\n ...(await fetchCustomerData(data.customerID)),\n },\n context: {\n // set a flag to prevent from running again\n triggerAfterChange: false,\n },\n })\n },\n ],\n },\n fields: [\n /* ... */\n ],\n}\n```\n\n## TypeScript\n\nThe default TypeScript interface for `context` is `{ [key: string]: unknown }`. If you prefer a more strict typing in your project or when authoring plugins for others, you can override this using the `declare` syntax.\n\nThis is known as \"type augmentation\", a TypeScript feature which allows us to add types to existing objects. Simply put this in any `.ts` or `.d.ts` file:\n\n```ts\nimport { RequestContext as OriginalRequestContext } from 'payload'\n\ndeclare module 'payload' {\n // Create a new interface that merges your additional fields with the original one\n export interface RequestContext extends OriginalRequestContext {\n myObject?: string\n // ...\n }\n}\n```\n\nThis will add the property `myObject` with a type of string to every context object. Make sure to follow this example correctly, as type augmentation can mess up your types if you do it wrong.\n"])</script><script>self.__next_f.push([1,"63:T4947,"])</script><script>self.__next_f.push([1,"\nThe Payload Local API gives you the ability to execute the same operations that are available through REST and GraphQL within Node, directly on your server. Here, you don't need to deal with server latency or network speed whatsoever and can interact directly with your database.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n \u003cbr /\u003e\n The Local API is incredibly powerful when used in React Server Components and other similar server-side contexts. With other headless CMS, you need to request your data from third-party servers via an HTTP layer, which can add significant loading time to your server-rendered pages. With Payload, you don't have to leave your server to gather the data you need. It can be incredibly fast and is definitely a game changer.\n\u003c/Banner\u003e\n\nHere are some common examples of how you can use the Local API:\n\n- Fetching Payload data within React Server Components\n- Seeding data via Node seed scripts that you write and maintain\n- Opening custom Next.js route handlers which feature additional functionality but still rely on Payload\n- Within [Access Control](../access-control) and [Hooks](../hooks/overview)\n\n## Accessing Payload\n\nYou can gain access to the currently running `payload` object via two ways:\n\n#### Accessing from args or `req`\n\nIn most places within Payload itself, you can access `payload` directly from the arguments of [Hooks](../hooks/overview), [Access Control](../access-control/overview), [Validation](../fields/overview#validations) functions, and similar. This is the simplest way to access Payload in most cases. Most config functions take the `req` (Request) object, which has Payload bound to it (`req.payload`).\n\nExample:\n\n```ts\nconst afterChangeHook: CollectionAfterChangeHook = async ({ req: { payload } }) =\u003e {\n const posts = await payload.find({\n collection: 'posts',\n })\n}\n```\n\n#### Importing it\n\nIf you want to import Payload in places where you don't have the option to access it from function arguments or `req`, you can import it and initialize it.\n\n```ts\nimport { getPayload } from 'payload'\nimport config from '@payload-config'\n\nconst payload = await getPayload({ config })\n```\n\nIf you're working in Next.js' development mode, Payload will work with Hot Module Replacement (HMR), and as you make changes to your Payload Config, your usage of Payload will always be in sync with your changes. In production, `getPayload` simply disables all HMR functionality so you don't need to write your code any differently. We handle optimization for you in production mode.\n\nIf you are accessing Payload via function arguments or `req.payload`, HMR is automatically supported if you are using it within Next.js.\n\nFor more information about using Payload outside of Next.js, [click here](./outside-nextjs).\n\n## Local options available\n\nYou can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are executed in.\n\n| Local Option | Description |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |\n| `data` | The data to use within the operation. Required for `create`, `update`. |\n| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |\n| `locale` | Specify [locale](../configuration/localization) for any returned documents. |\n| `select` | Specify [select](../queries/select) to control which fields to include to the result. |\n| `populate` | Specify [populate](../queries/select#populate) to control which fields to include to the result from populated documents. |\n| `fallbackLocale` | Specify a [fallback locale](../configuration/localization) to use for any returned documents. |\n| `overrideAccess` | Skip access control. By default, this property is set to true within all Local API operations. |\n| `overrideLock` | By default, document locks are ignored (`true`). Set to `false` to enforce locks and prevent operations when a document is locked by another user. [More details](../admin/locked-documents). |\n| `user` | If you set `overrideAccess` to `false`, you can pass a user to use against the access control checks. |\n| `showHiddenFields` | Opt-in to receiving hidden fields. By default, they are hidden from returned documents in accordance to your config. |\n| `pagination` | Set to false to return all documents and avoid querying for document counts. |\n| `context` | [Context](../hooks/context), which will then be passed to `context` and `req.context`, which can be read by hooks. Useful if you want to pass additional information to the hooks which shouldn't be necessarily part of the document, for example a `triggerBeforeChange` option which can be read by the BeforeChange hook to determine if it should run or not. |\n| `disableErrors` | When set to `true`, errors will not be thrown. Instead, the `findByID` operation will return `null`, and the `find` operation will return an empty documents array. |\n| `disableTransaction` | When set to `true`, a [database transactions](../database/transactions) will not be initialized. |\n\n_There are more options available on an operation by operation basis outlined below._\n\n## Transactions\n\nWhen your database uses transactions you need to thread req through to all local operations. Postgres uses transactions and MongoDB uses transactions when you are using replica sets. Passing req without transactions is still recommended.\n\n```js\nconst post = await payload.find({\n collection: 'posts',\n req, // passing req is recommended\n})\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n By default, all access control checks are disabled in the Local API, but you can re-enable them if\n you'd like, as well as pass a specific user to run the operation with.\n\u003c/Banner\u003e\n\n## Collections\n\nThe following Collection operations are available through the Local API:\n\n### Create#collection-create\n\n```js\n// The created Post document is returned\nconst post = await payload.create({\n collection: 'posts', // required\n data: {\n // required\n title: 'sure',\n description: 'maybe',\n },\n locale: 'en',\n fallbackLocale: false,\n user: dummyUserDoc,\n overrideAccess: true,\n showHiddenFields: false,\n\n // If creating verification-enabled auth doc,\n // you can optionally disable the email that is auto-sent\n disableVerificationEmail: true,\n\n // If your collection supports uploads, you can upload\n // a file directly through the Local API by providing\n // its full, absolute file path.\n filePath: path.resolve(__dirname, './path-to-image.jpg'),\n\n // Alternatively, you can directly pass a File,\n // if file is provided, filePath will be omitted\n file: uploadedFile,\n})\n```\n\n### Find#collection-find\n\n```js\n// Result will be a paginated set of Posts.\n// See /docs/queries/pagination for more.\nconst result = await payload.find({\n collection: 'posts', // required\n depth: 2,\n page: 1,\n limit: 10,\n pagination: false, // If you want to disable pagination count, etc.\n where: {}, // pass a `where` query here\n sort: '-title',\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n### Find by ID#collection-find-by-id\n\n```js\n// Result will be a Post document.\nconst result = await payload.findByID({\n collection: 'posts', // required\n id: '507f1f77bcf86cd799439011', // required\n depth: 2,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n### Count#collection-count\n\n```js\n// Result will be an object with:\n// {\n// totalDocs: 10, // count of the documents satisfies query\n// }\nconst result = await payload.count({\n collection: 'posts', // required\n locale: 'en',\n where: {}, // pass a `where` query here\n user: dummyUser,\n overrideAccess: false,\n})\n```\n\n### Update by ID#collection-update-by-id\n\n```js\n// Result will be the updated Post document.\nconst result = await payload.update({\n collection: 'posts', // required\n id: '507f1f77bcf86cd799439011', // required\n data: {\n // required\n title: 'sure',\n description: 'maybe',\n },\n depth: 2,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.\n showHiddenFields: true,\n\n // If your collection supports uploads, you can upload\n // a file directly through the Local API by providing\n // its full, absolute file path.\n filePath: path.resolve(__dirname, './path-to-image.jpg'),\n\n // If you are uploading a file and would like to replace\n // the existing file instead of generating a new filename,\n // you can set the following property to `true`\n overwriteExistingFiles: true,\n})\n```\n\n### Update Many#collection-update-many\n\n```js\n// Result will be an object with:\n// {\n// docs: [], // each document that was updated\n// errors: [], // each error also includes the id of the document\n// }\nconst result = await payload.update({\n collection: 'posts', // required\n where: {\n // required\n fieldName: { equals: 'value' },\n },\n data: {\n // required\n title: 'sure',\n description: 'maybe',\n },\n depth: 0,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.\n showHiddenFields: true,\n\n // If your collection supports uploads, you can upload\n // a file directly through the Local API by providing\n // its full, absolute file path.\n filePath: path.resolve(__dirname, './path-to-image.jpg'),\n\n // If you are uploading a file and would like to replace\n // the existing file instead of generating a new filename,\n // you can set the following property to `true`\n overwriteExistingFiles: true,\n})\n```\n\n### Delete#collection-delete\n\n```js\n// Result will be the now-deleted Post document.\nconst result = await payload.delete({\n collection: 'posts', // required\n id: '507f1f77bcf86cd799439011', // required\n depth: 2,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.\n showHiddenFields: true,\n})\n```\n\n### Delete Many#collection-delete-many\n\n```js\n// Result will be an object with:\n// {\n// docs: [], // each document that is now deleted\n// errors: [], // any errors that occurred, including the id of the errored on document\n// }\nconst result = await payload.delete({\n collection: 'posts', // required\n where: {\n // required\n fieldName: { equals: 'value' },\n },\n depth: 0,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.\n showHiddenFields: true,\n})\n```\n\n## Auth Operations\n\nIf a collection has [`Authentication`](../authentication/overview) enabled, additional Local API operations will be\navailable:\n\n### Login\n\n```js\n// result will be formatted as follows:\n// {\n// token: 'o38jf0q34jfij43f3f...', // JWT used for auth\n// user: { ... } // the user document that just logged in\n// exp: 1609619861 // the UNIX timestamp when the JWT will expire\n// }\n\nconst result = await payload.login({\n collection: 'users', // required\n data: {\n // required\n email: 'dev@payloadcms.com',\n password: 'rip',\n },\n req: req, // pass a Request object to be provided to all hooks\n res: res, // used to automatically set an HTTP-only auth cookie\n depth: 2,\n locale: 'en',\n fallbackLocale: false,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n### Forgot Password\n\n```js\n// Returned token will allow for a password reset\nconst token = await payload.forgotPassword({\n collection: 'users', // required\n data: {\n // required\n email: 'dev@payloadcms.com',\n },\n req: req, // pass a Request object to be provided to all hooks\n})\n```\n\n### Reset Password\n\n```js\n// Result will be formatted as follows:\n// {\n// token: 'o38jf0q34jfij43f3f...', // JWT used for auth\n// user: { ... } // the user document that just logged in\n// }\nconst result = await payload.resetPassword({\n collection: 'users', // required\n data: {\n // required\n password: req.body.password, // the new password to set\n token: 'afh3o2jf2p3f...', // the token generated from the forgotPassword operation\n },\n req: req, // pass a Request object to be provided to all hooks\n res: res, // used to automatically set an HTTP-only auth cookie\n})\n```\n\n### Unlock\n\n```js\n// Returned result will be a boolean representing success or failure\nconst result = await payload.unlock({\n collection: 'users', // required\n data: {\n // required\n email: 'dev@payloadcms.com',\n },\n req: req, // pass a Request object to be provided to all hooks\n overrideAccess: true,\n})\n```\n\n### Verify\n\n```js\n// Returned result will be a boolean representing success or failure\nconst result = await payload.verifyEmail({\n collection: 'users', // required\n token: 'afh3o2jf2p3f...', // the token saved on the user as `_verificationToken`\n})\n```\n\n## Globals\n\nThe following Global operations are available through the Local API:\n\n### Find#global-find\n\n```js\n// Result will be the Header Global.\nconst result = await payload.findGlobal({\n slug: 'header', // required\n depth: 2,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n### Update#global-update\n\n```js\n// Result will be the updated Header Global.\nconst result = await payload.updateGlobal({\n slug: 'header', // required\n data: {\n // required\n nav: [\n {\n url: 'https://google.com',\n },\n {\n url: 'https://payloadcms.com',\n },\n ],\n },\n depth: 2,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n overrideLock: false, // By default, document locks are ignored. Set to false to enforce locks.\n showHiddenFields: true,\n})\n```\n\n## TypeScript\n\nLocal API calls will automatically infer your [generated types](../typescript/generating-types).\n\nHere is an example of usage:\n\n```ts\n// Properly inferred as `Post` type\nconst post = await payload.create({\n collection: 'posts',\n\n // Data will now be typed as Post and give you type hints\n data: {\n title: 'my title',\n description: 'my description',\n },\n})\n```\n"])</script><script>self.__next_f.push([1,"64:Tbf4,"])</script><script>self.__next_f.push([1,"\nPayload can be used completely outside of Next.js which is helpful in cases like running scripts, using Payload in a separate backend service, or using Payload's Local API to fetch your data directly from your database in other frontend frameworks like SvelteKit, Remix, Nuxt, and similar.\n\n\u003cBanner\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr/\u003e\n Payload and all of its official packages are fully ESM. If you want to use Payload within your own projects, make sure you are writing your scripts in ESM format or dynamically importing the Payload Config.\n\u003c/Banner\u003e\n\n## Importing the Payload Config outside of Next.js\n\nPayload provides a convenient way to run standalone scripts, which can be useful for tasks like seeding your database or performing one-off operations.\n\nIn standalone scripts, you can simply import the Payload Config and use it right away. If you need an initialized copy of Payload, you can then use the `getPayload` function. This can be useful for tasks like seeding your database or performing other one-off operations.\n\n```ts\nimport { getPayload } from 'payload'\nimport config from '@payload-config'\n\nconst seed = async () =\u003e {\n // Get a local copy of Payload by passing your config\n const payload = await getPayload({ config })\n\n const user = await payload.create({\n collection: 'users',\n data: {\n email: 'dev@payloadcms.com',\n password: 'some-password'\n }\n })\n\n const page = await payload.create({\n collection: 'pages',\n data: {\n title: 'My Homepage',\n // other data to seed here\n }\n })\n}\n\n// Call the function here to run your seed script\nawait seed()\n```\n\nYou can then execute the script using `payload run`. Example: if you placed this standalone script in `src/seed.ts`, you would execute it like this:\n\n```sh\npayload run src/seed.ts\n```\n\nThe `payload run` command does two things for you:\n\n1. It loads the environment variables the same way Next.js loads them, eliminating the need for additional dependencies like `dotenv`. The usage of `dotenv` is not recommended, as Next.js loads environment variables differently. By using `payload run`, you ensure consistent environment variable handling across your Payload and Next.js setup.\n2. It initializes tsx, allowing direct execution of TypeScript files manually installing tools like tsx or ts-node.\n\n### Troubleshooting\n\nIf you encounter import-related errors, you have 2 options:\n\n#### Option 1: enable swc mode by appending `--use-swc` to the `payload` command:\n\nExample:\n```sh\npayload run src/seed.ts --use-swc\n```\n\nNote: Install @swc-node/register in your project first. While swc mode is faster than the default tsx mode, it might break for some imports.\n\n#### Option 2: use an alternative runtime like bun\n\nWhile we do not guarantee support for alternative runtimes, you are free to use them and disable payloads own transpilation by appending the `--disable-transpilation` flag to the `payload` command:\n\n```sh\nbunx --bun payload run src/seed.ts --disable-transpile\n```\n\nYou will need to have bun installed on your system for this to work.\n"])</script><script>self.__next_f.push([1,"65:T5730,"])</script><script>self.__next_f.push([1,"\n\u003cBanner\u003e\n A fully functional REST API is automatically generated from your Collection and Global configs.\n\u003c/Banner\u003e\n\nThe REST API is a fully functional HTTP client that allows you to interact with your Documents in a RESTful manner. It supports all CRUD operations and is equipped with automatic pagination, depth, and sorting.\nAll Payload API routes are mounted and prefixed to your config's `routes.api` URL segment (default: `/api`).\n\n**REST query parameters:**\n\n- [depth](../queries/depth) - automatically populates relationships and uploads\n- [locale](../configuration/localization#retrieving-localized-docs) - retrieves document(s) in a specific locale\n- [fallback-locale](../configuration/localization#retrieving-localized-docs) - specifies a fallback locale if no locale value exists\n- [select](../queries/select) - specifies which fields to include to the result\n- [populate](../queries/select#populate) - specifies which fields to include to the result from populated documents\n\n## Collections\n\nEach collection is mounted using its `slug` value. For example, if a collection's slug is `users`, all corresponding routes will be mounted on `/api/users`.\n\nNote: Collection slugs must be formatted in kebab-case\n\n**All CRUD operations are exposed as follows:**\n\n\u003cRestExamples\n data={[\n {\n operation: \"Find\",\n method: \"GET\",\n path: \"/api/{collection-slug}\",\n description: \"Find paginated documents\",\n example: {\n slug: \"getCollection\",\n req: true,\n res: {\n paginated: true,\n data: {\n id: \"644a5c24cc1383022535fc7c\",\n title: \"Home\",\n content: \"REST API examples\",\n slug: \"home\",\n createdAt: \"2023-04-27T11:27:32.419Z\",\n updatedAt: \"2023-04-27T11:27:32.419Z\",\n },\n },\n drawerContent: (\n \u003c\u003e\n \u003ch6\u003eAdditional \u003ccode\u003efind\u003c/code\u003e query parameters\u003c/h6\u003e\n The \u003ccode\u003efind\u003c/code\u003e endpoint supports the following additional query parameters:\n \u003cul\u003e\n \u003cli\u003e\n \u003ca href=\"../queries/overview#sort\"\u003esort\u003c/a\u003e - sort by field\n \u003c/li\u003e\n \u003cli\u003e\n \u003ca href=\"../queries/overview\"\u003ewhere\u003c/a\u003e - pass a where query to constrain returned\n documents\n \u003c/li\u003e\n \u003cli\u003e\n \u003ca href=\"../queries/pagination#pagination-controls\"\u003elimit\u003c/a\u003e - limit the returned\n documents to a certain number\n \u003c/li\u003e\n \u003cli\u003e\n \u003ca href=\"../queries/pagination#pagination-controls\"\u003epage\u003c/a\u003e - get a specific page of\n documents\n \u003c/li\u003e\n \u003c/ul\u003e\n \u003c/\u003e\n ),\n },\n },\n {\n operation: \"Find By ID\",\n method: \"GET\",\n path: \"/api/{collection-slug}/{id}\",\n description: \"Find a specific document by ID\",\n example: {\n slug: \"findByID\",\n req: true,\n res: {\n id: \"644a5c24cc1383022535fc7c\",\n title: \"Home\",\n content: \"REST API examples\",\n slug: \"home\",\n createdAt: \"2023-04-27T11:27:32.419Z\",\n updatedAt: \"2023-04-27T11:27:32.419Z\",\n },\n },\n },\n {\n operation: \"Count\",\n method: \"GET\",\n path: \"/api/{collection-slug}/count\",\n description: \"Count the documents\",\n example: {\n slug: \"count\",\n req: true,\n res: {\n totalDocs: 10\n },\n },\n },\n {\n operation: \"Create\",\n method: \"POST\",\n path: \"/api/{collection-slug}\",\n description: \"Create a new document\",\n example: {\n slug: \"createDocument\",\n req: {\n credentials: true,\n headers: true,\n body: {\n title: \"New page\",\n content: \"Here is some content\",\n },\n },\n res: {\n message: \"Page successfully created.\",\n doc: {\n id: \"644ba34c86359864f9535932\",\n title: \"New page\",\n content: \"Here is some content\",\n slug: \"new-page\",\n createdAt: \"2023-04-28T10:43:24.466Z\",\n updatedAt: \"2023-04-28T10:43:24.466Z\",\n },\n },\n },\n },\n {\n operation: \"Update\",\n method: \"PATCH\",\n path: \"/api/{collection-slug}\",\n description: \"Update all documents matching the where query\",\n example: {\n slug: \"updateDocument\",\n req: {\n credentials: true,\n query: true,\n headers: true,\n body: {\n title: \"I have been updated!\",\n },\n },\n res: {\n docs: [\n {\n id: \"644ba34c86359864f9535932\",\n title: \"I have been updated!\",\n content: \"Here is some content\",\n slug: \"new-page\",\n createdAt: \"2023-04-28T10:43:24.466Z\",\n updatedAt: \"2023-04-28T10:45:23.724Z\",\n },\n ],\n errors: [],\n },\n },\n },\n {\n operation: \"Update By ID\",\n method: \"PATCH\",\n path: \"/api/{collection-slug}/{id}\",\n description: \"Update a document by ID\",\n example: {\n slug: \"updateDocumentByID\",\n req: {\n credentials: true,\n headers: true,\n body: {\n title: \"I have been updated by ID!\",\n categories: \"example-uuid\",\n tags: {\n relationTo: \"location\",\n value: \"another-example-uuid\",\n },\n },\n },\n res: {\n message: \"Updated successfully.\",\n doc: {\n id: \"644a5c24cc1383022535fc7c\",\n title: \"I have been updated by ID!\",\n content: \"REST API examples\",\n categories: {\n id: \"example-uuid\",\n name: \"Test Category\",\n },\n tags: [\n {\n relationTo: \"location\",\n value: {\n id: \"another-example-uuid\",\n name: \"Test Location\",\n },\n },\n ],\n slug: \"home\",\n createdAt: \"2023-04-27T11:27:32.419Z\",\n updatedAt: \"2023-04-28T10:47:59.259Z\",\n },\n },\n },\n },\n {\n operation: \"Delete\",\n method: \"DELETE\",\n path: \"/api/{collection-slug}\",\n description: \"Delete all documents matching the where query\",\n example: {\n slug: \"deleteDocuments\",\n req: {\n credentials: true,\n query: true,\n headers: true,\n },\n res: {\n docs: [\n {\n id: \"644ba4cf86359864f953594b\",\n title: \"New page\",\n content: \"Here is some content\",\n slug: \"new-page\",\n createdAt: \"2023-04-28T10:49:51.359Z\",\n updatedAt: \"2023-04-28T10:49:51.359Z\",\n },\n ],\n errors: [],\n },\n },\n },\n {\n operation: \"Delete by ID\",\n method: \"DELETE\",\n path: \"/api/{collection-slug}/{id}\",\n description: \"Delete an existing document by ID\",\n example: {\n slug: \"deleteByID\",\n req: {\n credentials: true,\n headers: true,\n },\n res: {\n id: \"644ba51786359864f9535954\",\n title: \"New page\",\n content: \"Here is some content\",\n slug: \"new-page\",\n createdAt: \"2023-04-28T10:51:03.028Z\",\n updatedAt: \"2023-04-28T10:51:03.028Z\",\n },\n },\n },\n\n]}\n/\u003e\n\n## Auth Operations\n\nAuth enabled collections are also given the following endpoints:\n\n\u003cRestExamples\n data={[\n {\n operation: \"Login\",\n method: \"POST\",\n path: \"/api/{user-collection}/login\",\n description: \"Logs in a user with email / password\",\n example: {\n slug: \"login\",\n req: {\n credentials: true,\n headers: true,\n body: {\n email: \"dev@payloadcms.com\",\n password: \"password\",\n },\n },\n res: {\n message: \"Auth Passed\",\n user: {\n id: \"644b8453cd20c7857da5a9b0\",\n email: \"dev@payloadcms.com\",\n _verified: true,\n createdAt: \"2023-04-28T08:31:15.788Z\",\n updatedAt: \"2023-04-28T11:11:03.716Z\",\n },\n token: \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\",\n exp: 1682689147,\n },\n },\n },\n {\n operation: \"Logout\",\n method: \"POST\",\n path: \"/api/{user-collection}/logout\",\n description: \"Logs out a user\",\n example: {\n slug: \"logout\",\n req: {\n headers: true,\n credentials: true,\n },\n res: {\n message: \"You have been logged out successfully.\",\n },\n },\n },\n {\n operation: \"Unlock\",\n method: \"POST\",\n path: \"/api/{user-collection}/unlock\",\n description: \"Unlock a user account\",\n example: {\n slug: \"unlockCollection\",\n req: {\n credentials: true,\n headers: true,\n body: {\n email: \"dev@payloadcms.com\",\n },\n },\n res: {\n message: \"Success\",\n },\n },\n },\n {\n operation: \"Refresh\",\n method: \"POST\",\n path: \"/api/{user-collection}/refresh-token\",\n description: \"Refreshes a token that has not yet expired\",\n example: {\n slug: \"refreshToken\",\n req: {\n credentials: true,\n headers: true,\n },\n res: {\n message: \"Token refresh successful\",\n refreshedToken: \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\",\n exp: 1682689362,\n user: {\n email: \"dev@payloadcms.com\",\n id: \"644b8453cd20c7857da5a9b0\",\n collection: \"users\",\n },\n },\n },\n },\n {\n operation: \"Verify User\",\n method: \"POST\",\n path: \"/api/{user-collection}/verify/{token}\",\n description: \"User verification\",\n example: {\n slug: \"verifyUser\",\n req: {\n credentials: true,\n headers: true,\n },\n res: {\n message: \"Email verified successfully.\",\n },\n },\n },\n {\n operation: \"Current User\",\n method: \"GET\",\n path: \"/api/{user-collection}/me\",\n description: \"Returns the currently logged in user with token\",\n example: {\n slug: \"currentUser\",\n req: {\n credentials: true,\n headers: true,\n },\n res: {\n user: {\n id: \"644b8453cd20c7857da5a9b0\",\n email: \"dev@payloadcms.com\",\n _verified: true,\n createdAt: \"2023-04-28T08:31:15.788Z\",\n updatedAt: \"2023-04-28T11:45:23.926Z\",\n _strategy: \"local-jwt\",\n },\n collection: \"users\",\n token: \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\",\n exp: 1682689523,\n },\n },\n },\n {\n operation: \"Forgot Password\",\n method: \"POST\",\n path: \"/api/{user-collection}/forgot-password\",\n description: \"Password reset workflow entry point\",\n example: {\n slug: \"forgotPassword\",\n req: {\n headers: true,\n credentials: true,\n body: {\n email: \"dev@payloadcms.com\",\n },\n },\n res: {\n message: \"Success\",\n },\n },\n },\n {\n operation: \"Reset Password\",\n method: \"POST\",\n path: \"/api/{user-collection}/reset-password\",\n description: \"Reset user password\",\n example: {\n slug: \"resetPassword\",\n req: {\n credentials: true,\n headers: true,\n body: {\n token: \"7eac3830ffcfc7f9f66c00315dabeb11575dba91\",\n password: \"newPassword\",\n },\n },\n res: {\n message: \"Password reset successfully.\",\n token: \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\",\n user: {\n id: \"644baa473ea9538765cc30fc\",\n email: \"dev@payloadcms.com\",\n _verified: true,\n createdAt: \"2023-04-28T11:13:11.569Z\",\n updatedAt: \"2023-04-28T11:49:23.860Z\",\n },\n },\n },\n },\n\n]}\n/\u003e\n\n## Globals\n\nGlobals cannot be created or deleted, so there are only two REST endpoints opened:\n\n\u003cRestExamples\n data={[\n {\n operation: 'Get Global',\n method: 'GET',\n path: '/api/globals/{global-slug}',\n description: 'Get a global by slug',\n example: {\n slug: 'getGlobal',\n req: {\n credentials: true,\n headers: true,\n },\n res: {\n announcement: 'Here is an announcement!',\n globalType: 'announcement',\n createdAt: '2023-04-28T08:53:56.066Z',\n updatedAt: '2023-04-28T08:53:56.066Z',\n id: '644b89a496c64a833fe579c9',\n },\n },\n },\n {\n operation: 'Update Global',\n method: 'POST',\n path: '/api/globals/{global-slug}',\n description: 'Update a global by slug',\n example: {\n slug: 'updateGlobal',\n req: {\n headers: true,\n credentials: true,\n body: {\n announcement: 'Paging Doctor Scrunt',\n },\n },\n res: {\n announcement: 'Paging Doctor Scrunt',\n globalType: 'announcement',\n createdAt: '2023-04-28T08:53:56.066Z',\n updatedAt: '2023-04-28T08:53:56.066Z',\n id: '644b89a496c64a833fe579c9',\n },\n },\n },\n ]}\n/\u003e\n\n## Preferences\n\nIn addition to the dynamically generated endpoints above Payload also has REST endpoints to manage the admin user [preferences](../admin/preferences) for data specific to the authenticated user.\n\n\u003cRestExamples\n data={[\n {\n operation: 'Get Preference',\n method: 'GET',\n path: '/api/payload-preferences/{key}',\n description: 'Get a preference by key',\n example: {\n slug: 'getPreference',\n req: {\n headers: true,\n credentials: true,\n },\n res: {\n _id: '644bb7a8307b3d363c6edf2c',\n key: 'region',\n user: '644b8453cd20c7857da5a9b0',\n userCollection: 'users',\n __v: 0,\n createdAt: '2023-04-28T12:10:16.689Z',\n updatedAt: '2023-04-28T12:10:16.689Z',\n value: 'Europe/London',\n },\n },\n },\n {\n operation: 'Create Preference',\n method: 'POST',\n path: '/api/payload-preferences/{key}',\n description: 'Create or update a preference by key',\n example: {\n slug: 'createPreference',\n req: {\n headers: true,\n credentials: true,\n body: {\n value: 'Europe/London',\n },\n },\n res: {\n message: 'Updated successfully.',\n doc: {\n user: '644b8453cd20c7857da5a9b0',\n key: 'region',\n userCollection: 'users',\n value: 'Europe/London',\n },\n },\n },\n },\n {\n operation: 'Delete Preference',\n method: 'DELETE',\n path: '/api/payload-preferences/{key}',\n description: 'Delete a preference by key',\n example: {\n slug: 'deletePreference',\n req: {\n headers: true,\n },\n res: {\n message: 'deletedSuccessfully',\n },\n },\n },\n ]}\n/\u003e\n\n## Custom Endpoints\n\nAdditional REST API endpoints can be added to your application by providing an array of `endpoints` in various places within a Payload Config. Custom endpoints are useful for adding additional middleware on existing routes or for building custom functionality into Payload apps and plugins. Endpoints can be added at the top of the Payload Config, `collections`, and `globals` and accessed respective of the api and slugs you have configured.\n\n\u003cBanner type=\"warning\"\u003e\n Custom endpoints are not authenticated by default. You are responsible for securing your own endpoints.\n\u003c/Banner\u003e\n\n\nEach endpoint object needs to have:\n\n| Property | Description |\n| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`path`** | A string for the endpoint route after the collection or globals slug |\n| **`method`** | The lowercase HTTP verb to use: 'get', 'head', 'post', 'put', 'delete', 'connect' or 'options' |\n| **`handler`** | A function or array of functions to be called with **req**, **res** and **next** arguments. [Next.js](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) |\n| **`root`** | When `true`, defines the endpoint on the root Next.js app, bypassing Payload handlers and the `routes.api` subpath. Note: this only applies to top-level endpoints of your Payload Config, endpoints defined on `collections` or `globals` cannot be root. |\n| **`custom`** | Extension point for adding custom data (e.g. for plugins) |\n\nExample:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\n// a collection of 'orders' with an additional route for tracking details, reachable at /api/orders/:id/tracking\nexport const Orders: CollectionConfig = {\n slug: 'orders',\n fields: [\n /* ... */\n ],\n // highlight-start\n endpoints: [\n {\n path: '/:id/tracking',\n method: 'get',\n handler: async (req) =\u003e {\n const tracking = await getTrackingInfo(req.routeParams.id)\n\n if (!tracking) {\n return Response.json({ error: 'not found' }, { status: 404})\n }\n\n return Response.json({\n message: `Hello ${req.routeParams.name as string} @ ${req.routeParams.group as string}`,\n })\n },\n },\n {\n path: '/:id/tracking',\n method: 'post',\n handler: async (req) =\u003e {\n // `data` is not automatically appended to the request\n // if you would like to read the body of the request\n // you can use `data = await req.json()`\n const data = await req.json()\n await req.payload.update({\n collection: 'tracking',\n data: {\n // data to update the document with\n }\n })\n return Response.json({\n message: 'successfully updated tracking info'\n })\n }\n },\n {\n path: '/:id/forbidden',\n method: 'post',\n handler: async (req) =\u003e {\n // this is an example of an authenticated endpoint\n if (!req.user) {\n return Response.json({ error: 'forbidden' }, { status: 403 })\n }\n\n // do something\n\n return Response.json({\n message: 'successfully updated tracking info'\n })\n }\n }\n ],\n // highlight-end\n}\n```\n\n\u003cBanner\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n **req** will have the **payload** object and can be used inside your endpoint handlers for making calls like req.payload.find() that will make use of [Access Control](../access-control/overview) and [Hooks](../hooks/overview).\n\u003c/Banner\u003e\n\n#### Helpful tips\n`req.data`\n\nData is not automatically appended to the request. You can read the body data by calling `await req.json()`.\n\nOr you could use our helper function that mutates the request and appends data and file if found.\n\n```ts\nimport { addDataAndFileToRequest } from '@payloadcms/next/utilities'\n\n// custom endpoint example\n{\n path: '/:id/tracking',\n method: 'post',\n handler: async (req) =\u003e {\n await addDataAndFileToRequest(req)\n await req.payload.update({\n collection: 'tracking',\n data: {\n // data to update the document with\n }\n })\n return Response.json({\n message: 'successfully updated tracking info'\n })\n }\n}\n```\n\n`req.locale` \u0026 `req.fallbackLocale`\n\nThe locale and the fallback locale are not automatically appended to custom endpoint requests. If you would like to add them you can use this helper function.\n\n```ts\nimport { addLocalesToRequestFromData } from '@payloadcms/next/utilities'\n\n// custom endpoint example\n{\n path: '/:id/tracking',\n method: 'post',\n handler: async (req) =\u003e {\n await addLocalesToRequestFromData(req)\n // you now can access req.locale \u0026 req.fallbackLocale\n return Response.json({ message: 'success' })\n }\n}\n```\n\n## Method Override for GET Requests\n\nPayload supports a method override feature that allows you to send GET requests using the HTTP POST method. This can be particularly useful in scenarios when the query string in a regular GET request is too long.\n\n### How to Use\n\nTo use this feature, include the `X-HTTP-Method-Override` header set to `GET` in your POST request. The parameters should be sent in the body of the request with the `Content-Type` set to `application/x-www-form-urlencoded`.\n\n### Example\n\nHere is an example of how to use the method override to perform a GET request:\n\n#### Using Method Override (POST)\n\n```ts\nconst res = await fetch(`${api}/${collectionSlug}`, {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Accept-Language': i18n.language,\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'X-HTTP-Method-Override': 'GET',\n },\n body: qs.stringify({\n depth: 1,\n locale: 'en',\n }),\n})\n```\n\n#### Equivalent Regular GET Request\n\n```ts\nconst res = await fetch(`${api}/${collectionSlug}?depth=1\u0026locale=en`, {\n method: 'GET',\n credentials: 'include',\n headers: {\n 'Accept-Language': i18n.language,\n },\n})\n```\n"])</script><script>self.__next_f.push([1,"66:T153b,"])</script><script>self.__next_f.push([1,"\nIn addition to its REST and Local APIs, Payload ships with a fully featured and extensible GraphQL API.\n\nBy default, the GraphQL API is exposed via `/api/graphql`, but you can customize this URL via specifying your `routes` within the main Payload Config.\n\nThe labels you provide for your Collections and Globals are used to name the GraphQL types that are created to correspond to your config. Special characters and spaces are removed.\n\n## GraphQL Options\n\nAt the top of your Payload Config you can define all the options to manage GraphQL.\n\n| Option | Description |\n| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |\n| `mutations` | Any custom Mutations to be added in addition to what Payload provides. [More](../graphql/extending) |\n| `queries` | Any custom Queries to be added in addition to what Payload provides. [More](../graphql/extending) |\n| `maxComplexity` | A number used to set the maximum allowed complexity allowed by requests [More](../graphql/overview#query-complexity-limits) |\n| `disablePlaygroundInProduction` | A boolean that if false will enable the GraphQL playground, defaults to true. [More](../graphql/overview#graphql-playground) |\n| `disable` | A boolean that if true will disable the GraphQL entirely, defaults to false. |\n\n## Collections\n\nEverything that can be done to a Collection via the REST or Local API can be done with GraphQL (outside of uploading files, which is REST-only). If you have a collection as follows:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const PublicUser: CollectionConfig = {\n slug: 'public-users',\n auth: true, // Auth is enabled\n fields: [\n ...\n ],\n}\n```\n\n**Payload will automatically open up the following queries:**\n\n| Query Name | Operation |\n| ------------------ | ------------------- |\n| `PublicUser` | `findByID` |\n| `PublicUsers` | `find` |\n| `countPublicUsers` | `count` |\n| `mePublicUser` | `me` auth operation |\n\n**And the following mutations:**\n\n| Query Name | Operation |\n| ------------------------------ | ------------------------------- |\n| `createPublicUser` | `create` |\n| `updatePublicUser` | `update` |\n| `deletePublicUser` | `delete` |\n| `forgotPasswordPublicUser` | `forgotPassword` auth operation |\n| `resetPasswordPublicUser` | `resetPassword` auth operation |\n| `unlockPublicUser` | `unlock` auth operation |\n| `verifyPublicUser` | `verify` auth operation |\n| `loginPublicUser` | `login` auth operation |\n| `logoutPublicUser` | `logout` auth operation |\n| `refreshTokenPublicUser` | `refresh` auth operation |\n\n## Globals\n\nGlobals are also fully supported. For example:\n\n```ts\nimport type { GlobalConfig } from 'payload';\n\nconst Header: GlobalConfig = {\n slug: 'header',\n fields: [\n ...\n ],\n}\n```\n\n**Payload will open the following query:**\n\n| Query Name | Operation |\n| ------------ | --------- |\n| `Header` | `findOne` |\n\n**And the following mutation:**\n\n| Query Name | Operation |\n| ------------------ | --------- |\n| `updateHeader` | `update` |\n\n## Preferences\n\nUser [preferences](../admin/overview#preferences) for the [Admin Panel](../admin/overview) are also available to GraphQL the same way as other collection schemas are generated. To query preferences you must supply an authorization token in the header and only the preferences of that user will be accessible.\n\n**Payload will open the following query:**\n\n| Query Name | Operation |\n| ---------------- | --------- |\n| `Preference` | `findOne` |\n\n**And the following mutations:**\n\n| Query Name | Operation |\n| ---------------------- | --------- |\n| `updatePreference` | `update` |\n| `deletePreference` | `delete` |\n\n## GraphQL Playground\n\nGraphQL Playground is enabled by default for development purposes, but disabled in production. You can enable it in production by passing `graphQL.disablePlaygroundInProduction` a `false` setting in the main Payload Config.\n\nYou can even log in using the `login[collection-singular-label-here]` mutation to use the Playground as an authenticated user.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n \u003cbr /\u003e\n To see more regarding how the above queries and mutations are used, visit your GraphQL playground\n (by default at\n [`${SERVER_URL}/api/graphql-playground`](http://localhost:3000/api/graphql-playground))\n while your server is running. There, you can use the \"Schema\" and \"Docs\" buttons on the right to\n see a ton of detail about how GraphQL operates within Payload.\n\u003c/Banner\u003e\n\n## Query complexity limits\n\nPayload comes with a built-in query complexity limiter to prevent bad people from trying to slow down your server by running massive queries. To learn more, [click here](../production/preventing-abuse#limiting-graphql-complexity).\n"])</script><script>self.__next_f.push([1,"67:T14fe,"])</script><script>self.__next_f.push([1,"\nYou can add your own GraphQL queries and mutations to Payload, making use of all the types that Payload has defined for you.\n\nTo do so, add your queries and mutations to the main Payload Config as follows:\n\n| Config Path | Description |\n| ------------------- | --------------------------------------------------------------------------- |\n| `graphQL.queries` | Function that returns an object containing keys to custom GraphQL queries |\n| `graphQL.mutations` | Function that returns an object containing keys to custom GraphQL mutations |\n\nThe above properties each receive a function that is defined with the following arguments:\n\n**`GraphQL`**\n\nThis is Payload's GraphQL dependency. You should not install your own copy of GraphQL as a dependency due to underlying restrictions based on how GraphQL works. Instead, you can use the Payload-provided copy via this argument.\n\n**`payload`**\n\nThis is a copy of the currently running Payload instance, which provides you with existing GraphQL types for all of your Collections and Globals - among other things.\n\n## Return value\n\nBoth `graphQL.queries` and `graphQL.mutations` functions should return an object with properties equal to your newly written GraphQL queries and mutations.\n\n## Example\n\n`payload.config.js`:\n\n```ts\nimport { buildConfig } from 'payload'\nimport myCustomQueryResolver from './graphQL/resolvers/myCustomQueryResolver'\n\nexport default buildConfig({\n graphQL: {\n // highlight-start\n queries: (GraphQL, payload) =\u003e {\n return {\n MyCustomQuery: {\n type: new GraphQL.GraphQLObjectType({\n name: 'MyCustomQuery',\n fields: {\n text: {\n type: GraphQL.GraphQLString,\n },\n someNumberField: {\n type: GraphQL.GraphQLFloat,\n },\n },\n }),\n args: {\n argNameHere: {\n type: new GraphQL.GraphQLNonNull(GraphQLString),\n },\n },\n resolve: myCustomQueryResolver,\n },\n }\n },\n // highlight-end\n },\n})\n```\n\n## Resolver function\n\nIn your resolver, make sure you set `depth: 0` if you're returning data directly from the local API so that GraphQL can correctly resolve queries to nested values such as relationship data.\n\nYour function will receive four arguments you can make use of:\n\nExample\n\n```ts\n;async (obj, args, context, info) =\u003e {}\n```\n\n**`obj`**\n\nThe previous object. Not very often used and usually discarded.\n\n**`args`**\n\nThe available arguments from your query or mutation will be available to you here, these must be configured via the custom operation first.\n\n**`context`**\n\nAn object containing the `req` and `res` objects that will provide you with the `payload`, `user` instances and more, like any other Payload API handler.\n\n**`info`**\n\nContextual information about the currently running GraphQL operation. You can get schema information from this as well as contextual information about where this resolver function is being run.\n\n## Types\n\nWe've exposed a few types and utilities to help you extend the API further. Payload uses the GraphQL.js package for which you can view the full list of available types in the [official documentation](https://graphql.org/graphql-js/type/).\n\n**`GraphQLJSON`** \u0026 **`GraphQLJSONObject`**\n\n```ts\nimport { GraphQLJSON, GraphQLJSONObject } from '@payloadcms/graphql/types'\n```\n\n**`GraphQL`**\n\nYou can directly import the GraphQL package used by Payload, most useful for typing.\n\n```ts\nimport { GraphQL } from '@payloadcms/graphql/types'\n```\n\n\u003cBanner type=\"warning\"\u003e\n For queries, mutations and handlers make sure you use the `GraphQL` and `payload` instances provided via arguments.\n\u003c/Banner\u003e\n\n\n**`buildPaginatedListType`**\n\nThis is a utility function that allows you to build a new GraphQL type for a paginated result similar to the Payload's generated schema.\nIt takes in two arguments, the first for the name of this new schema type and the second for the GraphQL type to be used in the docs parameter.\n\nExample\n\n```ts\nimport { buildPaginatedListType } from '@payloadcms/graphql/types'\n\nexport const getMyPosts = (GraphQL, payload) =\u003e {\n return {\n args: {},\n resolve: Resolver,\n // The name of your new type has to be unique\n type: buildPaginatedListType('AuthorPosts', payload.collections['posts'].graphQL?.type),\n }\n}\n```\n\n**`payload.collections.slug.graphQL`**\n\nIf you want to extend more of the provided API then the `graphQL` object on your collection slug will contain additional types to help you re-use code for types, mutations and queries.\n\n```ts\ngraphQL?: {\n type: GraphQLObjectType\n paginatedType: GraphQLObjectType\n JWT: GraphQLObjectType\n versionType: GraphQLObjectType\n whereInputType: GraphQLInputObjectType\n mutationInputType: GraphQLNonNull\u003cany\u003e\n updateMutationInputType: GraphQLNonNull\u003cany\u003e\n}\n```\n\n## Best practices\n\nThere are a few ways to structure your code, we recommend using a dedicated `graphql` directory so you can keep all of your logic in one place. You have total freedom of how you want to structure this but a common pattern is to group functions by type and with their resolver.\n\nExample\n\n```\nsrc/graphql\n---- queries/\n index.ts\n -- myCustomQuery/\n index.ts\n resolver.ts\n\n---- mutations/\n```\n"])</script><script>self.__next_f.push([1,"68:T7d6,\nIn Payload the schema is controlled by your collections and globals. All you need to do is run the generate command and the entire schema will be created for you.\n\n## Schema generation script\n\nInstall `@payloadcms/graphql` as a dev dependency:\n\n```bash\npnpm add @payloadcms/graphql -D\n```\n\nRun the following command to generate the schema:\n\n```bash\npnpm payload-graphql generate:schema\n```\n\n## Custom Field Schemas\n\nFor `array`, `block`, `group` and named `tab` fields, you can generate top level reusable interfaces. The following group field config:\n\n```ts\n{\n type: 'group',\n name: 'meta',\n interfaceName: 'SharedMeta', // highlight-line\n fields: [\n {\n name: 'title',\n type: 'text',\n },\n {\n name: 'description',\n type: 'text',\n },\n ],\n}\n```\n\nwill generate:\n\n```ts\n// A top level reusable type will be generated\ntype SharedMeta {\n title: String\n description: String\n}\n\n// And will be referenced inside the generated schema\ntype Collection1 {\n // ...other fields\n meta: SharedMeta\n}\n```\n\nThe above example outputs all your definitions to a file relative from your payload config as `./graphql/schema.graphql`. By default, the file will be output to your current working directory as `schema.graphql`.\n\n### Adding an NPM script\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant\u003c/strong\u003e\n \u003cbr /\u003e\n Payload needs to be able to find your config to generate your GraphQL schema.\n\u003c/Banner\u003e\n\nPayload will automatically try and locate your config, but might not always be able to find it. For example, if you are working in a `/src` directory or similar, you need to tell Payload where to find your config manually by using an environment variable.\n\nIf this applies to you, create an NPM script to make generating types easier:\n\n```json\n// package.json\n\n{\n \"scripts\": {\n \"generate:graphQLSchema\": \"cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload-graphql generate:schema\"\n }\n}\n```\n\nNow you can run `pnpm generate:graphQLSchema` to easily generate your schema.\n69:T207a,"])</script><script>self.__next_f.push([1,"\nIn Payload, \"querying\" means filtering or searching through Documents within a [Collection](../configuration/collections). The querying language in Payload is designed to be simple and powerful, allowing you to filter Documents with extreme precision through an intuitive and standardized structure.\n\nPayload provides three common APIs for querying your data:\n\n- [Local API](../local-api/overview) - Extremely fast, direct-to-database access\n- [REST API](../rest-api/overview) - Standard HTTP endpoints for querying and mutating data\n- [GraphQL](../graphql/overview) - A full GraphQL API with a GraphQL Playground\n\nEach of these APIs share the same underlying querying language, and fully support all of the same features. This means that you can learn Payload's querying language once, and use it across any of the APIs that you might use.\n\nTo query your Documents, you can send any number of [Operators](#operators) through your request:\n\n```ts\nconst query = {\n color: {\n equals: 'blue',\n },\n}\n```\n\n_The exact query syntax will depend on the API you are using, but the concepts are the same across all APIs. [More details](#writing-queries)._\n\n\u003cBanner\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n You can also use queries within [Access Control](../access-control/overview) functions.\n\u003c/Banner\u003e\n\n## Operators\n\nThe following operators are available for use in queries:\n\n| Operator | Description |\n| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `equals` | The value must be exactly equal. |\n| `not_equals` | The query will return all documents where the value is not equal. |\n| `greater_than` | For numeric or date-based fields. |\n| `greater_than_equal` | For numeric or date-based fields. |\n| `less_than` | For numeric or date-based fields. |\n| `less_than_equal` | For numeric or date-based fields. |\n| `like` | Case-insensitive string must be present. If string of words, all words must be present, in any order. |\n| `contains` | Must contain the value entered, case-insensitive. |\n| `in` | The value must be found within the provided comma-delimited list of values. |\n| `not_in` | The value must NOT be within the provided comma-delimited list of values. |\n| `all` | The value must contain all values provided in the comma-delimited list. |\n| `exists` | Only return documents where the value either exists (`true`) or does not exist (`false`). |\n| `near` | For distance related to a [Point Field](../fields/point) comma separated as `\u003clongitude\u003e, \u003clatitude\u003e, \u003cmaxDistance in meters (nullable)\u003e, \u003cminDistance in meters (nullable)\u003e`. |\n| `within` | For [Point Fields](../fields/point) to filter documents based on whether points are inside of the given area defined in GeoJSON. [Example](../fields/point#querying-within) |\n| `intersects` | For [Point Fields](../fields/point) to filter documents based on whether points intersect with the given area defined in GeoJSON. [Example](../fields/point#querying-intersects) |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n If you know your users will be querying on certain fields a lot, add `index: true` to the Field Config. This will speed up searches using that field immensely.\n\u003c/Banner\u003e\n\n### And / Or Logic\n\nIn addition to defining simple queries, you can join multiple queries together using AND / OR logic. These can be nested as deeply as you need to create complex queries.\n\nTo join queries, use the `and` or `or` keys in your query object:\n\n```ts\nconst query = {\n or: [ // highlight-line\n {\n color: {\n equals: 'mint',\n },\n },\n {\n and: [ // highlight-line\n {\n color: {\n equals: 'white',\n },\n },\n {\n featured: {\n equals: false,\n },\n },\n ],\n },\n ],\n}\n```\n\nWritten in plain English, if the above query were passed to a `find` operation, it would translate to finding posts where either the `color` is `mint` OR the `color` is `white` AND `featured` is set to false.\n\n### Nested properties\n\nWhen working with nested properties, which can happen when using relational fields, it is possible to use the dot notation to access the nested property. For example, when working with a `Song` collection that has a `artists` field which is related to an `Artists` collection using the `name: 'artists'`. You can access a property within the collection `Artists` like so:\n\n```js\nconst query = {\n 'artists.featured': {\n // nested property name to filter on\n exists: true, // operator to use and boolean value that needs to be true\n },\n}\n```\n\n## Writing Queries\n\nWriting queries in Payload is simple and consistent across all APIs, with only minor differences in syntax between them.\n\n### Local API\n\nThe [Local API](../local-api/overview) supports the `find` operation that accepts a raw query object:\n\n```ts\nconst getPosts = async () =\u003e {\n const posts = await payload.find({\n collection: 'posts',\n where: {\n color: {\n equals: 'mint',\n },\n },\n })\n\n return posts\n}\n```\n\n### GraphQL API\n\nAll `find` queries in the [GraphQL API](../graphql/overview) support the `where` argument that accepts a raw query object:\n\n```ts\nquery {\n Posts(where: { color: { equals: mint } }) {\n docs {\n color\n }\n totalDocs\n }\n}\n```\n\n### REST API\n\nWith the [REST API](../rest-api/overview), you can use the full power of Payload queries, but they are written as query strings instead:\n\n**`https://localhost:3000/api/posts?where[color][equals]=mint`**\n\nTo understand the syntax, you need to understand that complex URL search strings are parsed into a JSON object. This one isn't too bad, but more complex queries get unavoidably more difficult to write.\n\nFor this reason, we recommend to use the extremely helpful and ubiquitous [`qs-esm`](https://www.npmjs.com/package/qs-esm) package to parse your JSON / object-formatted queries into query strings:\n\n```ts\nimport { stringify } from 'qs-esm'\n\nconst query = {\n color: {\n equals: 'mint',\n },\n // This query could be much more complex\n // and QS would handle it beautifully\n}\n\nconst getPosts = async () =\u003e {\n const stringifiedQuery = stringify(\n {\n where: query, // ensure that `qs` adds the `where` property, too!\n },\n { addQueryPrefix: true },\n )\n\n const response = await fetch(`http://localhost:3000/api/posts${stringifiedQuery}`)\n // Continue to handle the response below...\n}\n```\n"])</script><script>self.__next_f.push([1,"6a:T8ce,"])</script><script>self.__next_f.push([1,"\nDocuments in Payload can be easily sorted by a specific [Field](../fields/overview). When querying Documents, you can pass the name of any top-level field, and the response will sort the Documents by that field in _ascending_ order. If prefixed with a minus symbol (\"-\"), they will be sorted in _descending_ order. In Local API multiple fields can be specified by using an array of strings. In REST API multiple fields can be specified by separating fields with comma. The minus symbol can be in front of individual fields.\n\nBecause sorting is handled by the database, the field cannot be a [Virtual Field](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges). It must be stored in the database to be searchable.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n For performance reasons, it is recommended to enable `index: true` for the fields that will be sorted upon. [More details](../fields/overview).\n\u003c/Banner\u003e\n\n## Local API\n\nTo sort Documents in the [Local API](../local-api/overview), you can use the `sort` option in your query:\n\n```ts\nconst getPosts = async () =\u003e {\n const posts = await payload.find({\n collection: 'posts',\n sort: '-createdAt', // highlight-line\n })\n\n return posts\n}\n```\n\nTo sort by multiple fields, you can use the `sort` option with fields in an array:\n\n```ts\nconst getPosts = async () =\u003e {\n const posts = await payload.find({\n collection: 'posts',\n sort: ['priority', '-createdAt'], // highlight-line\n })\n\n return posts\n}\n```\n\n## REST API\n\nTo sort in the [REST API](../rest-api/overview), you can use the `sort` parameter in your query:\n\n```ts\nfetch('https://localhost:3000/api/posts?sort=-createdAt') // highlight-line\n .then((response) =\u003e response.json())\n .then((data) =\u003e console.log(data))\n```\n\nTo sort by multiple fields, you can use the `sort` parameter with fields separated by comma:\n\n```ts\nfetch('https://localhost:3000/api/posts?sort=priority,-createdAt') // highlight-line\n .then((response) =\u003e response.json())\n .then((data) =\u003e console.log(data))\n```\n\n## GraphQL API\n\nTo sort in the [GraphQL API](../graphql/overview), you can use the `sort` parameter in your query:\n\n```ts\nquery {\n Posts(sort: \"-createdAt\") {\n docs {\n color\n }\n }\n}\n```\n"])</script><script>self.__next_f.push([1,"6b:Ta47,"])</script><script>self.__next_f.push([1,"\nDocuments in Payload can have relationships to other Documents. This is true for both [Collections](../configuration/collections) as well as [Globals](../configuration/globals). When you query a Document, you can specify the depth at which to populate any of its related Documents either as full objects, or only their IDs.\n\nDepth will optimize the performance of your application by limiting the amount of processing made in the database and significantly reducing the amount of data returned. Since Documents can be infinitely nested or recursively related, it's important to be able to control how deep your API populates.\n\nFor example, when you specify a `depth` of `0`, the API response might look like this:\n\n```json\n{\n \"id\": \"5ae8f9bde69e394e717c8832\",\n \"title\": \"This is a great post\",\n \"author\": \"5f7dd05cd50d4005f8bcab17\"\n}\n```\n\nBut with a `depth` of `1`, the response might look like this:\n\n```json\n{\n \"id\": \"5ae8f9bde69e394e717c8832\",\n \"title\": \"This is a great post\",\n \"author\": {\n \"id\": \"5f7dd05cd50d4005f8bcab17\",\n \"name\": \"John Doe\"\n }\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n Depth has no effect in the [GraphQL API](../graphql/overview), because there, depth is based on the shape of your queries.\n\u003c/Banner\u003e\n\n## Local API\n\nTo specify depth in the [Local API](../local-api/overview), you can use the `depth` option in your query:\n\n```ts\nconst getPosts = async () =\u003e {\n const posts = await payload.find({\n collection: 'posts',\n depth: 2, // highlight-line\n })\n\n return posts\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n This is the same for [Globals](../configuration/globals) using the `findGlobal` operation.\n\u003c/Banner\u003e\n\n## REST API\n\nTo specify depth in the [REST API](../rest-api/overview), you can use the `depth` parameter in your query:\n\n```ts\nfetch('https://localhost:3000/api/posts?depth=2') // highlight-line\n .then((res) =\u003e res.json())\n .then((data) =\u003e console.log(data))\n```\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n This is the same for [Globals](../configuration/globals) using the `/api/globals` endpoint.\n\u003c/Banner\u003e\n\n## Max Depth\n\nFields like the [Relationship Field](../fields/relationship) or the [Upload Field](../fields/upload) can also set a maximum depth. If exceeded, this will limit the population depth regardless of what the depth might be on the request.\n\nTo set a max depth for a field, use the `maxDepth` property in your field configuration:\n\n```js\n{\n slug: 'posts',\n fields: [\n {\n name: 'author',\n type: 'relationship',\n relationTo: 'users',\n maxDepth: 2, // highlight-line\n }\n ]\n}\n```\n"])</script><script>self.__next_f.push([1,"6c:T14c7,"])</script><script>self.__next_f.push([1,"\nBy default, Payload's APIs will return _all fields_ for a given collection or global. But, you may not need all of that data for all of your queries. Sometimes, you might want just a few fields from the response, which can speed up the Payload API and reduce the amount of JSON that is sent to you from the API.\n\nThis is where Payload's `select` feature comes in. Here, you can define exactly which fields you'd like to retrieve from the API.\n\n## Local API\n\nTo specify `select` in the [Local API](../local-api/overview), you can use the `select` option in your query:\n\n```ts\n// Include mode\nconst getPosts = async () =\u003e {\n const posts = await payload.find({\n collection: 'posts',\n select: {\n text: true,\n // select a specific field from group\n group: {\n number: true\n },\n // select all fields from array\n array: true,\n }, // highlight-line\n })\n\n return posts\n}\n\n// Exclude mode\nconst getPosts = async () =\u003e {\n const posts = await payload.find({\n collection: 'posts',\n // Select everything except for array and group.number\n select: {\n array: false,\n group: {\n number: false\n }\n }, // highlight-line\n })\n\n return posts\n}\n```\n\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n To perform querying with `select` efficiently, Payload implements your `select` query on the database level. Because of that, your `beforeRead` and `afterRead` hooks may not receive the full `doc`.\n\u003c/Banner\u003e\n\n\n## REST API\n\nTo specify select in the [REST API](../rest-api/overview), you can use the `select` parameter in your query:\n\n```ts\nfetch('https://localhost:3000/api/posts?select[color]=true\u0026select[group][number]=true') // highlight-line\n .then((res) =\u003e res.json())\n .then((data) =\u003e console.log(data))\n```\n\nTo understand the syntax, you need to understand that complex URL search strings are parsed into a JSON object. This one isn't too bad, but more complex queries get unavoidably more difficult to write.\n\nFor this reason, we recommend to use the extremely helpful and ubiquitous [`qs-esm`](https://www.npmjs.com/package/qs-esm) package to parse your JSON / object-formatted queries into query strings:\n\n```ts\nimport { stringify } from 'qs-esm'\n\nconst select = {\n text: true,\n group: {\n number: true\n }\n // This query could be much more complex\n // and QS would handle it beautifully\n}\n\nconst getPosts = async () =\u003e {\n const stringifiedQuery = stringify(\n {\n select, // ensure that `qs` adds the `select` property, too!\n },\n { addQueryPrefix: true },\n )\n\n const response = await fetch(`http://localhost:3000/api/posts${stringifiedQuery}`)\n // Continue to handle the response below...\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n This is the same for [Globals](../configuration/globals) using the `/api/globals` endpoint.\n\u003c/Banner\u003e\n\n\n## defaultPopulate collection config property\n\nThe `defaultPopulate` property allows you specify which fields to select when populating the collection from another document.\nThis is especially useful for links where only the `slug` is needed instead of the entire document.\n\nWith this feature, you can dramatically reduce the amount of JSON that is populated from [Relationship](../fields/relationship) or [Upload](../fields/upload) fields.\n\nFor example, in your content model, you might have a `Link` field which links out to a different page. When you go to retrieve these links, you really only need the `slug` of the page.\n\nLoading all of the page content, its related links, and everything else is going to be overkill and will bog down your Payload APIs. Instead, you can define the `defaultPopulate` property on your `Pages` collection, so that when Payload \"populates\" a related Page, it only selects the `slug` field and therefore returns significantly less JSON:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nimport { lexicalEditor, LinkFeature } from '@payloadcms/richtext-lexical'\nimport { slateEditor } from '@payloadcms/richtext-slate'\n\n// The TSlug generic can be passed to have type safety for `defaultPopulate`.\n// If avoided, the `defaultPopulate` type resolves to `SelectType`.\nexport const Pages: CollectionConfig\u003c'pages'\u003e = {\n slug: 'pages',\n // Specify `select`.\n defaultPopulate: {\n slug: true,\n },\n fields: [\n {\n name: 'slug',\n type: 'text',\n required: true,\n },\n ],\n}\n```\n\n## populate\n\nSetting `defaultPopulate` will enforce that each time Payload performs a \"population\" of a related document, only the fields specified will be queried and returned. However, you can override `defaultPopulate` with the `populate` property in the Local and REST API:\n\n**Local API:**\n\n```ts\nconst getPosts = async () =\u003e {\n const posts = await payload.find({\n collection: 'posts',\n populate: {\n // Select only `text` from populated docs in the \"pages\" collection\n // Now, no matter what the `defaultPopulate` is set to on the \"pages\" collection,\n // it will be overridden, and the `text` field will be returned instead.\n pages: {\n text: true,\n }, // highlight-line\n },\n })\n\n return posts\n}\n```\n\n**REST API:**\n\n```ts\nfetch('https://localhost:3000/api/posts?populate[pages][text]=true') // highlight-line\n .then((res) =\u003e res.json())\n .then((data) =\u003e console.log(data))\n```\n"])</script><script>self.__next_f.push([1,"6d:T8f6,"])</script><script>self.__next_f.push([1,"\nAll collection `find` queries are paginated automatically. Responses are returned with top-level meta data related to pagination, and returned documents are nested within a `docs` array.\n\n**`Find` response properties:**\n\n| Property | Description |\n| ------------- | --------------------------------------------------------- |\n| docs | Array of documents in the collection |\n| totalDocs | Total available documents within the collection |\n| limit | Limit query parameter - defaults to `10` |\n| totalPages | Total pages available, based upon the `limit` queried for |\n| page | Current page number |\n| pagingCounter | `number` of the first doc on the current page |\n| hasPrevPage | `true/false` if previous page exists |\n| hasNextPage | `true/false` if next page exists |\n| prevPage | `number` of previous page, `null` if it doesn't exist |\n| nextPage | `number` of next page, `null` if it doesn't exist |\n\n**Example response:**\n\n```json\n{\n // Document Array // highlight-line\n \"docs\": [\n {\n \"title\": \"Page Title\",\n \"description\": \"Some description text\",\n \"priority\": 1,\n \"createdAt\": \"2020-10-17T01:19:29.858Z\",\n \"updatedAt\": \"2020-10-17T01:19:29.858Z\",\n \"id\": \"5f8a46a1dd05db75c3c64760\"\n }\n ],\n // Metadata // highlight-line\n \"totalDocs\": 6,\n \"limit\": 1,\n \"totalPages\": 6,\n \"page\": 1,\n \"pagingCounter\": 1,\n \"hasPrevPage\": false,\n \"hasNextPage\": true,\n \"prevPage\": null,\n \"nextPage\": 2\n}\n```\n\n## Pagination controls\n\nAll Payload APIs support the pagination controls below. With them, you can create paginated lists of documents within your application:\n\n| Control | Description |\n| ------- | --------------------------------------- |\n| `limit` | Limits the number of documents returned |\n| `page` | Get a specific page number |\n\n### Disabling pagination within Local API\n\nFor `find` operations within the Local API, you can disable pagination to retrieve all documents from a collection by passing `pagination: false` to the `find` local operation.\n"])</script><script>self.__next_f.push([1,"6e:T3be0,"])</script><script>self.__next_f.push([1,"\nPayload dynamically generates a beautiful, [fully type-safe](../typescript/overview) Admin Panel to manage your users and data. It is highly performant, even with 100+ fields, and is translated in over 30 languages. Within the Admin Panel you can manage content, [render your site](../live-preview/overview), preview drafts, [diff versions](../versions/overview), and so much more.\n\nThe Admin Panel is designed to [white-label your brand](https://payloadcms.com/blog/white-label-admin-ui). You can endlessly customize and extend the Admin UI by swapping in your own [Custom Components](./components)—everything from simple field labels to entire views can be modified or replaced to perfectly tailor the interface for your editors.\n\nThe Admin Panel is written in [TypeScript](https://www.typescriptlang.org) and built with [React](https://react.dev) using the [Next.js App Router](https://nextjs.org/docs/app). It supports [React Server Components](https://react.dev/reference/rsc/server-components), enabling the use of the [Local API](../local-api/overview) on the front-end. You can install Payload into any [existing Next.js app in just one line](../getting-started/installation) and [deploy it anywhere](../production).\n\n\u003cBanner type=\"success\"\u003e\n The Payload Admin Panel is designed to be as minimal and straightforward as possible to allow easy customization and control. [Learn more](./components).\n\u003c/Banner\u003e\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/admin.jpg\"\n srcDark=\"https://payloadcms.com/images/docs/admin-dark.jpg\"\n alt=\"Admin Panel with collapsible sidebar\"\n caption=\"Redesigned Admin Panel with a collapsible sidebar that's open by default, providing greater extensibility and enhanced horizontal real estate.\"\n/\u003e\n\n## Project Structure\n\nThe Admin Panel serves as the entire HTTP layer for Payload, providing a full CRUD interface for your app. This means that both the [REST](../rest-api/overview) and [GraphQL](../graphql/overview) APIs are simply [Next.js Routes](https://nextjs.org/docs/app/building-your-application/routing) that exist directly alongside your front-end application.\n\nOnce you [install Payload](../getting-started/installation), the following files and directories will be created in your app:\n\n```plaintext\napp/\n├─ (payload)/\n├── admin/\n├─── [[...segments]]/\n├──── page.tsx\n├──── not-found.tsx\n├── api/\n├─── [...slug]/\n├──── route.ts\n├── graphql/\n├──── route.ts\n├── graphql-playground/\n├──── route.ts\n├── custom.scss\n├── layout.tsx\n```\n\n\u003cBanner type=\"info\"\u003e\n If you are not familiar with Next.js project structure, you can [learn more about it here](https://nextjs.org/docs/getting-started/project-structure).\n\u003c/Banner\u003e\n\nAs shown above, all Payload routes are nested within the `(payload)` route group. This creates a boundary between the Admin Panel and the rest of your application by scoping all layouts and styles. The `layout.tsx` file within this directory, for example, is where Payload manages the `html` tag of the document to set proper [`lang`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang) and [`dir`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/dir) attributes, etc.\n\nThe `admin` directory contains all the _pages_ related to the interface itself, whereas the `api` and `graphql` directories contains all the _routes_ related to the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview). All admin routes are [easily configurable](#customizing-routes) to meet your application's exact requirements.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n If you don't use the [REST API](../rest/overview) or [GraphQL API](../graphql/overview), you can delete the [Next.js files corresponding to those routes](../admin/overview#project-structure), however, the overhead of this API is completely constrained to these endpoints, and will not slow down or affect Payload outside of the endpoints.\n\u003c/Banner\u003e\n\nFinally, the `custom.scss` file is where you can add or override globally-oriented styles in the Admin Panel, such as modify the color palette. Customizing the look and feel through CSS alone is a powerful feature of the Admin Panel, [more on that here](./customizing-css).\n\nAll auto-generated files will contain the following comments at the top of each file:\n\n```tsx\n/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */,\n/* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */\n```\n\n## Admin Options\n\nAll options for the Admin Panel are defined in your [Payload Config](../configuration/overview) under the `admin` property:\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: { // highlight-line\n // ...\n },\n})\n```\n\nThe following options are available:\n\n| Option | Description |\n|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`avatar`** | Set account profile picture. Options: `gravatar`, `default` or a custom React component. |\n| **`autoLogin`** | Used to automate log-in for dev and demonstration convenience. [More details](../authentication/overview). |\n| **`buildPath`** | Specify an absolute path for where to store the built Admin bundle used in production. Defaults to `path.resolve(process.cwd(), 'build')`. |\n| **`components`** | Component overrides that affect the entirety of the Admin Panel. [More details](./components). |\n| **`custom`** | Any custom properties you wish to pass to the Admin Panel. |\n| **`dateFormat`** | The date format that will be used for all dates within the Admin Panel. Any valid [date-fns](https://date-fns.org/) format pattern can be used. |\n| **`disable`** | If set to `true`, the entire Admin Panel will be disabled. |\n| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |\n| **`meta`** | Base metadata to use for the Admin Panel. [More details](./metadata). |\n| **`routes`** | Replace built-in Admin Panel routes with your own custom routes. [More details](#customizing-routes). |\n| **`theme`** | Restrict the Admin Panel theme to use only one of your choice. Default is `all`.\n| **`user`** | The `slug` of the Collection that you want to allow to login to the Admin Panel. [More details](#the-admin-user-collection). |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n These are the _root-level_ options for the Admin Panel. You can also customize [Collection Admin Options](./collections) and [Global Admin Options](./globals) through their respective `admin` keys.\n\u003c/Banner\u003e\n\n### The Admin User Collection\n\nTo specify which Collection to allow to login to the Admin Panel, pass the `admin.user` key equal to the slug of any auth-enabled Collection:\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n user: 'admins', // highlight-line\n },\n})\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n \u003cbr /\u003e\n The Admin Panel can only be used by a single auth-enabled Collection. To enable authentication for a Collection, simply set `auth: true` in the Collection's configuration. See [Authentication](../authentication/overview) for more information.\n\u003c/Banner\u003e\n\nBy default, if you have not specified a Collection, Payload will automatically provide a `User` Collection with access to the Admin Panel. You can customize or override the fields and settings of the default `User` Collection by adding your own Collection with `slug: 'users'`. Doing this will force Payload to use your provided `User` Collection instead of its default version.\n\nYou can use whatever Collection you'd like to access the Admin Panel as long as the Collection supports [Authentication](../authentication/overview). It doesn't need to be called `users`. For example, you may wish to have two Collections that both support authentication:\n\n- `admins` - meant to have a higher level of permissions to manage your data and access the Admin Panel\n- `customers` - meant for end users of your app that should not be allowed to log into the Admin Panel\n\nTo do this, specify `admin: { user: 'admins' }` in your config. This will provide access to the Admin Panel to only `admins`. Any users authenticated as `customers` will be prevented from accessing the Admin Panel. See [Access Control](../access-control/overview) for full details.\n\n### Role-based Access Control\n\nIt is also possible to allow multiple user types into the Admin Panel with limited permissions, known as role-based access control (RBAC). For example, you may wish to have two roles within the `admins` Collection:\n\n- `super-admin` - full access to the Admin Panel to perform any action\n- `editor` - limited access to the Admin Panel to only manage content\n\nTo do this, add a `roles` or similar field to your auth-enabled Collection, then use the `access.admin` property to grant or deny access based on the value of that field. See [Access Control](../access-control/overview) for full details. For a complete, working example of role-based access control, check out the official [Auth Example](https://github.com/payloadcms/payload/tree/main/examples/auth).\n\n## Customizing Routes\n\nYou have full control over the routes that Payload binds itself to. This includes both [Root-level Routes](#root-level-routes) such as the [REST API](../rest-api/overview), and [Admin-level Routes](#admin-level-routes) such as the user's account page. You can customize these routes to meet the needs of your application simply by specifying the desired paths in your config.\n\n### Root-level Routes\n\nRoot-level routes are those that are not behind the `/admin` path, such as the [REST API](../rest-api/overview) and [GraphQL API](../graphql/overview), or the root path of the Admin Panel itself.\n\nTo customize root-level routes, use the `routes` property in your [Payload Config](../configuration/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n routes: {\n admin: '/custom-admin-route' // highlight-line\n }\n})\n```\n\nThe following options are available:\n\n| Option | Default route | Description |\n|---------------------|-----------------------|---------------------------------------------------|\n| `admin` | `/admin` | The Admin Panel itself. |\n| `api` | `/api` | The [REST API](../rest-api/overview) base path. |\n| `graphQL` | `/graphql` | The [GraphQL API](../graphql/overview) base path. |\n| `graphQLPlayground` | `/graphql-playground` | The GraphQL Playground. |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n You can easily add _new_ routes to the Admin Panel through [Custom Endpoints](../rest-api/overview#custom-endpoints) and [Custom Views](./views).\n\u003c/Banner\u003e\n\n#### Customizing Root-level Routes\n\nYou can change the Root-level Routes as needed, such as to mount the Admin Panel at the root of your application.\n\nChanging Root-level Routes also requires a change to [Project Structure](#project-structure) to match the new route. For example, if you set `routes.admin` to `/`, you would need to completely remove the `admin` directory from the project structure:\n\n```plaintext\napp/\n├─ (payload)/\n├── [[...segments]]/\n├──── ...\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n If you set Root-level Routes _before_ auto-generating the Admin Panel via `create-payload-app`, your [Project Structure](#project-structure) will already be set up correctly.\n\u003c/Banner\u003e\n\n### Admin-level Routes\n\nAdmin-level routes are those behind the `/admin` path. These are the routes that are part of the Admin Panel itself, such as the user's account page, the login page, etc.\n\nTo customize admin-level routes, use the `admin.routes` property in your [Payload Config](../configuration/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n routes: {\n account: '/my-account' // highlight-line\n }\n },\n})\n```\n\nThe following options are available:\n\n| Option | Default route | Description |\n| ----------------- | ----------------------- | ----------------------------------------------- |\n| `account` | `/account` | The user's account page. |\n| `createFirstUser` | `/create-first-user` | The page to create the first user. |\n| `forgot` | `/forgot` | The password reset page. |\n| `inactivity` | `/logout-inactivity` | The page to redirect to after inactivity. |\n| `login` | `/login` | The login page. |\n| `logout` | `/logout` | The logout page. |\n| `reset` | `/reset` | The password reset page. |\n| `unauthorized` | `/unauthorized` | The unauthorized page. |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n You can also swap out entire _views_ out for your own, using the `admin.views` property of the Payload Config. See [Custom Views](./views) for more information.\n\u003c/Banner\u003e\n\n## I18n\n\nThe Payload Admin Panel is translated in over [30 languages and counting](https://github.com/payloadcms/payload/tree/main/packages/translations). Languages are automatically detected based on the user's browser and used by the Admin Panel to display all text in that language. If no language was detected, or if the user's language is not yet supported, English will be chosen. Users can easily specify their language by selecting one from their account page. See [I18n](../configuration/i18n) for more information.\n\n## Light and Dark Modes\n\nUsers in the Admin Panel have the ability to choose between light mode and dark mode for their editing experience. Users can select their preferred theme from their account page. Once selected, it is saved to their user's preferences and persisted across sessions and devices. If no theme was selected, the Admin Panel will automatically detect the operation system's theme and use that as the default.\n"])</script><script>self.__next_f.push([1,"6f:T2c24,"])</script><script>self.__next_f.push([1,"\nThe behavior of [Collections](../configuration/collections) within the [Admin Panel](./overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](./components), selecting which fields to display in the List View, and more.\n\nTo configure Admin Options for Collections, use the `admin` property in your Collection Config:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const MyCollection: CollectionConfig = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\n## Admin Options\n\nThe following options are available:\n\n| Option | Description |\n| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |\n| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Collection from navigation and admin routing. |\n| **`hooks`** | Admin-specific hooks for this Collection. [More details](../hooks/collections). |\n| **`useAsTitle`** | Specify a top-level field to use for a document title throughout the Admin Panel. If no field is defined, the ID of the document is used as the title. A field with `virtual: true` cannot be used as the title. |\n| **`description`** | Text to display below the Collection label in the List View to give editors more information. Alternatively, you can use the `admin.components.Description` to render a React component. [More details](#custom-components). |\n| **`defaultColumns`** | Array of field names that correspond to which columns to show by default in this Collection's List View. |\n| **`hideAPIURL`** | Hides the \"API URL\" meta field while editing documents within this Collection. |\n| **`enableRichTextLink`** | The [Rich Text](../fields/rich-text) field features a `Link` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |\n| **`enableRichTextRelationship`** | The [Rich Text](../fields/rich-text) field features a `Relationship` element which allows for users to automatically reference related documents within their rich text. Set to `true` by default. |\n| **`meta`** | Page metadata overrides to apply to this Collection within the Admin Panel. [More details](./metadata). |\n| **`preview`** | Function to generate preview URLs within the Admin Panel that can point to your app. [More details](#preview). |\n| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |\n| **`components`** | Swap in your own React components to be used within this Collection. [More details](#custom-components). |\n| **`listSearchableFields`** | Specify which fields should be searched in the List search view. [More details](#list-searchable-fields). |\n| **`pagination`** | Set pagination-specific options for this Collection. [More details](#pagination). |\n| **`baseListFilter`** | You can define a default base filter for this collection's List view, which will be merged into any filters that the user performs. |\n\n### Custom Components\n\nCollections can set their own [Custom Components](./components) which only apply to [Collection](../configuration/collections)-specific UI within the [Admin Panel](./overview). This includes elements such as the Save Button, or entire layouts such as the Edit View.\n\nTo override Collection Components, use the `admin.components` property in your [Collection Config](../configuration/collections):\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollection: SanitizedCollectionConfig = {\n // ...\n admin: {\n components: { // highlight-line\n // ...\n },\n },\n}\n```\n\nThe following options are available:\n\n| Path | Description |\n| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |\n| **`beforeList`** | An array of components to inject _before_ the built-in List View |\n| **`beforeListTable`** | An array of components to inject _before_ the built-in List View's table |\n| **`afterList`** | An array of components to inject _after_ the built-in List View |\n| **`afterListTable`** | An array of components to inject _after_ the built-in List View's table\n| **`Description`** | A component to render below the Collection label in the List View. An alternative to the `admin.description` property. |\n| **`edit.SaveButton`** | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. |\n| **`edit.SaveDraftButton`** | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. |\n| **`edit.PublishButton`** | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. |\n| **`edit.PreviewButton`** | Replace the default Preview Button with a Custom Component. [Preview](#preview) must be enabled. |\n| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).\n\u003c/Banner\u003e\n\n### Preview\n\nIt is possible to display a Preview Button within the Edit View of the Admin Panel. This will allow editors to visit the frontend of your app the corresponds to the document they are actively editing. This way they can preview the latest, potentially unpublished changes.\n\nTo configure the Preview Button, set the `admin.preview` property to a function in your [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Posts: CollectionConfig = {\n // ...\n admin: {\n // highlight-start\n preview: (doc, { locale }) =\u003e {\n if (doc?.slug) {\n return `/${doc.slug}?locale=${locale}`\n }\n\n return null\n },\n // highlight-end\n },\n}\n```\n\nThe preview function receives two arguments:\n\n| Argument | Description |\n| --- | --- |\n| **`doc`** | The Document being edited. |\n| **`ctx`** | An object containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT. |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n For fully working example of this, check of the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/main/examples/draft-preview) in the [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples).\n\u003c/Banner\u003e\n\n### Pagination\n\nAll Collections receive their own List View which displays a paginated list of documents that can be sorted and filtered. The pagination behavior of the List View can be customized on a per-Collection basis, and uses the same [Pagination](../queries/pagination) API that Payload provides.\n\nTo configure pagination options, use the `admin.pagination` property in your [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Posts: CollectionConfig = {\n // ...\n admin: {\n // highlight-start\n pagination: {\n defaultLimit: 10,\n limits: [10, 20, 50],\n },\n // highlight-end\n },\n}\n```\n\nThe following options are available:\n\n| Option | Description |\n| -------------- | --------------------------------------------------------------------------------------------------- |\n| `defaultLimit` | Integer that specifies the default per-page limit that should be used. Defaults to 10. |\n| `limits` | Provide an array of integers to use as per-page options for admins to choose from in the List View. |\n\n### List Searchable Fields\n\nIn the List View, there is a \"search\" box that allows you to quickly find a document through a simple text search. By default, it searches on the ID field. If defined, the `admin.useAsTitle` field is used. Or, you can explicitly define which fields to search based on the needs of your application.\n\nTo define which fields should be searched, use the `admin.listSearchableFields` property in your [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Posts: CollectionConfig = {\n // ...\n admin: {\n // highlight-start\n listSearchableFields: ['title', 'slug'],\n // highlight-end\n },\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n If you are adding `listSearchableFields`, make sure you index each of these fields so your admin queries can remain performant.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"70:T1475,"])</script><script>self.__next_f.push([1,"\nThe behavior of [Globals](../configuration/globals) within the [Admin Panel](./overview) can be fully customized to fit the needs of your application. This includes grouping or hiding their navigation links, adding [Custom Components](./components), setting page metadata, and more.\n\nTo configure Admin Options for Globals, use the `admin` property in your Global Config:\n\n```ts\nimport { GlobalConfig } from 'payload'\n\nexport const MyGlobal: GlobalConfig = {\n // ...\n admin: { // highlight-line\n // ...\n },\n}\n```\n\n## Admin Options\n\nThe following options are available:\n\n| Option | Description |\n| ------------- | --------------------------------------------------------------------------------------------------------------------------------- |\n| **`group`** | Text used as a label for grouping Collection and Global links together in the navigation. |\n| **`hidden`** | Set to true or a function, called with the current user, returning true to exclude this Global from navigation and admin routing. |\n| **`components`** | Swap in your own React components to be used within this Global. [More details](#custom-components). |\n| **`preview`** | Function to generate a preview URL within the Admin Panel for this Global that can point to your app. [More details](#preview). |\n| **`livePreview`** | Enable real-time editing for instant visual feedback of your front-end application. [More details](../live-preview/overview). |\n| **`hideAPIURL`** | Hides the \"API URL\" meta field while editing documents within this collection. |\n| **`meta`** | Page metadata overrides to apply to this Global within the Admin Panel. [More details](./metadata). |\n\n### Custom Components\n\nGlobals can set their own [Custom Components](./components) which only apply to [Global](../configuration/globals)-specific UI within the [Admin Panel](./overview). This includes elements such as the Save Button, or entire layouts such as the Edit View.\n\nTo override Global Components, use the `admin.components` property in your [Global Config](../configuration/globals):\n\n```ts\nimport type { SanitizedGlobalConfig } from 'payload'\n\nexport const MyGlobal: SanitizedGlobalConfig = {\n // ...\n admin: {\n components: { // highlight-line\n // ...\n },\n },\n}\n```\n\nThe following options are available:\n\n| Path | Description |\n| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |\n| **`elements.SaveButton`** | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. |\n| **`elements.SaveDraftButton`** | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. |\n| **`elements.PublishButton`** | Replace the default Publish Button with a Custom Component. [Drafts](../versions/drafts) must be enabled. |\n| **`elements.PreviewButton`** | Replace the default Preview Button with a Custom Component. [Preview](#preview) must be enabled. |\n| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).\n\u003c/Banner\u003e\n\n### Preview\n\nIt is possible to display a Preview Button within the Edit View of the Admin Panel. This will allow editors to visit the frontend of your app the corresponds to the document they are actively editing. This way they can preview the latest, potentially unpublished changes.\n\nTo configure the Preview Button, set the `admin.preview` property to a function in your Global Config:\n\n```ts\nimport { GlobalConfig } from 'payload'\n\nexport const MainMenu: GlobalConfig = {\n // ...\n admin: {\n // highlight-start\n preview: (doc, { locale }) =\u003e {\n if (doc?.slug) {\n return `/${doc.slug}?locale=${locale}`\n }\n\n return null\n },\n // highlight-end\n },\n}\n```\n\nThe preview function receives two arguments:\n\n| Argument | Description |\n| --- | --- |\n| **`doc`** | The Document being edited. |\n| **`ctx`** | An object containing `locale` and `token` properties. The `token` is the currently logged-in user's JWT. |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n For fully working example of this, check of the official [Draft Preview Example](https://github.com/payloadcms/payload/tree/main/examples/draft-preview) in the [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples).\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"71:T5732,"])</script><script>self.__next_f.push([1,"\nThe Payload [Admin Panel](./overview) is designed to be as minimal and straightforward as possible to allow for easy customization and full control over the UI. In order for Payload to support this level of customization, Payload provides a pattern for you to supply your own React components through your [Payload Config](../configuration/overview).\n\nAll Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default. This enables the use of the [Local API](../local-api/overview) directly on the front-end. Custom Components are available for nearly every part of the Admin Panel for extreme granularity and control.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n Client Components continue to be fully supported. To use Client Components in your app, simply include the `use client` directive. Payload will automatically detect and remove all default, [non-serializable props](https://react.dev/reference/rsc/use-client#serializable-types) before rendering your component. [More details](#client-components).\n\u003c/Banner\u003e\n\nThere are four main types of Custom Components in Payload:\n\n- [Root Components](#root-components)\n- [Collection Components](./collections#custom-components)\n- [Global Components](./globals#custom-components)\n- [Field Components](./fields#custom-components)\n\nTo swap in your own Custom Component, first consult the list of available components, determine the scope that corresponds to what you are trying to accomplish, then [author your React component(s)](#building-custom-components) accordingly.\n\n## Defining Custom Components\n\nAs Payload compiles the Admin Panel, it checks your config for Custom Components. When detected, Payload either replaces its own default component with yours, or if none exists by default, renders yours outright. While are many places where Custom Components are supported in Payload, each is defined in the same way using [Component Paths](#component-paths).\n\nTo add a Custom Component, point to its file path in your Payload Config:\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n components: {\n logout: {\n Button: '/src/components/Logout#MyComponent' // highlight-line\n }\n }\n },\n})\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n All Custom Components can be either Server Components or Client Components, depending on the presence of the `use client` directive at the top of the file.\n\u003c/Banner\u003e\n\n### Component Paths\n\nIn order to ensure the Payload Config is fully Node.js compatible and as lightweight as possible, components are not directly imported into your config. Instead, they are identified by their file path for the Admin Panel to resolve on its own.\n\nComponent Paths, by default, are relative to your project's base directory. This is either your current working directory, or the directory specified in `config.admin.baseDir`. To simplify Component Paths, you can also configure the base directory using the `admin.importMap.baseDir` property.\n\nComponents using named exports are identified either by appending `#` followed by the export name, or using the `exportName` property. If the component is the default export, this can be omitted.\n\n```ts\nimport { buildConfig } from 'payload'\nimport { fileURLToPath } from 'node:url'\nimport path from 'path'\nconst filename = fileURLToPath(import.meta.url)\nconst dirname = path.dirname(filename)\n\nconst config = buildConfig({\n // ...\n admin: {\n importMap: {\n baseDir: path.resolve(dirname, 'src'), // highlight-line\n },\n components: {\n logout: {\n Button: '/components/Logout#MyComponent' // highlight-line\n }\n }\n },\n})\n```\n\nIn this example, we set the base directory to the `src` directory, and omit the `/src/` part of our component path string.\n\n### Config Options\n\nWhile Custom Components are usually defined as a string, you can also pass in an object with additional options:\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n components: {\n logout: {\n // highlight-start\n Button: {\n path: '/src/components/Logout',\n exportName: 'MyComponent',\n }\n // highlight-end\n }\n }\n },\n})\n```\n\nThe following options are available:\n\n| Property | Description |\n|---------------|-------------------------------------------------------------------------------------------------------------------------------|\n| **`clientProps`** | Props to be passed to the Custom Components if it's a Client Component. [More details](#custom-props). |\n| **`exportName`** | Instead of declaring named exports using `#` in the component path, you can also omit them from `path` and pass them in here. |\n| **`path`** | File path to the Custom Component. Named exports can be appended to the end of the path, separated by a `#`. |\n| **`serverProps`** | Props to be passed to the Custom Component if it's a Server Component. [More details](#custom-props). |\n\nFor more details on how to build Custom Components, see [Building Custom Components](#building-custom-components).\n\n### Import Map\n\nIn order for Payload to make use of [Component Paths](#component-paths), an \"Import Map\" is automatically generated at `app/(payload)/admin/importMap.js`. This file contains every Custom Component in your config, keyed to their respective paths. When Payload needs to lookup a component, it uses this file to find the correct import.\n\nThe Import Map is automatically regenerated at startup and whenever Hot Module Replacement (HMR) runs, or you can run `payload generate:importmap` to manually regenerate it.\n\n#### Custom Imports\n\nIf needed, custom items can be appended onto the Import Map. This is mostly only relevant for plugin authors who need to add a custom import that is not referenced in a known location.\n\nTo add a custom import to the Import Map, use the `admin.dependencies` property in your [Payload Config](../getting-started/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n admin: {\n // ...\n dependencies: {\n myTestComponent: { // myTestComponent is the key - can be anything\n path: '/components/TestComponent.js#TestComponent',\n type: 'component',\n clientProps: {\n test: 'hello',\n },\n },\n },\n }\n}\n```\n\n## Building Custom Components\n\nAll Custom Components in Payload are [React Server Components](https://react.dev/reference/rsc/server-components) by default. This enables the use of the [Local API](../local-api/overview) directly on the front-end, among other things.\n\n### Default Props\n\nTo make building Custom Components as easy as possible, Payload automatically provides common props, such as the [`payload`](../local-api/overview) class and the [`i18n`](../configuration/i18n) object. This means that when building Custom Components within the Admin Panel, you do not have to get these yourself.\n\nHere is an example:\n\n```tsx\nimport React from 'react'\n\nconst MyServerComponent = async ({\n payload // highlight-line\n}) =\u003e {\n const page = await payload.findByID({\n collection: 'pages',\n id: '123',\n })\n\n return (\n \u003cp\u003e{page.title}\u003c/p\u003e\n )\n}\n```\n\nEach Custom Component receives the following props by default:\n\n| Prop | Description |\n| ------------------------- | ----------------------------------------------------------------------------------------------------- |\n| `payload` | The [Payload](../local-api/overview) class. |\n| `i18n` | The [i18n](../configuration/i18n) object. |\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n All Custom Components also receive various other props that are specific component being rendered. See [Root Components](#root-components), [Collection Components](./collections#custom-components), [Global Components](./globals#custom-components), or [Field Components](./fields#custom-components) for a complete list of all default props per component.\n\u003c/Banner\u003e\n\n### Custom Props\n\nTo pass in custom props from the config, you can use either the `clientProps` or `serverProps` properties depending on whether your prop is [serializable](https://react.dev/reference/rsc/use-client#serializable-types), and whether your component is a Server or Client Component.\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: { // highlight-line\n components: {\n logout: {\n Button: {\n path: '/src/components/Logout#MyComponent',\n clientProps: {\n myCustomProp: 'Hello, World!' // highlight-line\n },\n }\n }\n }\n },\n})\n```\n\n```tsx\n'use client'\nimport React from 'react'\n\nexport const MyComponent = ({ myCustomProp }: { myCustomProp: string }) =\u003e {\n return (\n \u003cbutton\u003e{myCustomProp}\u003c/button\u003e\n )\n}\n```\n\n### Client Components\n\nWhen [Building Custom Components](#building-custom-components), it's still possible to use client-side code such as `useState` or the `window` object. To do this, simply add the `use client` directive at the top of your file. Payload will automatically detect and remove all default, [non-serializable props](https://react.dev/reference/rsc/use-client#serializable-types) before rendering your component.\n\n```tsx\n'use client' // highlight-line\nimport React, { useState } from 'react'\n\nexport const MyClientComponent: React.FC = () =\u003e {\n const [count, setCount] = useState(0)\n\n return (\n \u003cbutton onClick={() =\u003e setCount(count + 1)}\u003e\n Clicked {count} times\n \u003c/button\u003e\n )\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n Client Components cannot be passed [non-serializable props](https://react.dev/reference/rsc/use-client#serializable-types). If you are rendering your Client Component _from within_ a Server Component, ensure that its props are serializable.\n\u003c/Banner\u003e\n\n### Accessing the Payload Config\n\nFrom any Server Component, the [Payload Config](../configuration/overview) can be accessed directly from the `payload` prop:\n\n```tsx\nimport React from 'react'\n\nexport default async function MyServerComponent({\n payload: {\n config // highlight-line\n }\n}) {\n return (\n \u003cLink href={config.serverURL}\u003e\n Go Home\n \u003c/Link\u003e\n )\n}\n```\n\nBut, the Payload Config is [non-serializable](https://react.dev/reference/rsc/use-client#serializable-types) by design. It is full of custom validation functions, React components, etc. This means that the Payload Config, in its entirety, cannot be passed directly to Client Components.\n\nFor this reason, Payload creates a Client Config and passes it into the Config Provider. This is a serializable version of the Payload Config that can be accessed from any Client Component via the [`useConfig`](./hooks#useconfig) hook:\n\n```tsx\n'use client'\nimport React from 'react'\nimport { useConfig } from '@payloadcms/ui'\n\nexport const MyClientComponent: React.FC = () =\u003e {\n const { config: { serverURL } } = useConfig() // highlight-line\n\n return (\n \u003cLink href={serverURL}\u003e\n Go Home\n \u003c/Link\u003e\n )\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n See [Using Hooks](#using-hooks) for more details.\n\u003c/Banner\u003e\n\nAll [Field Components](./fields) automatically receive their respective Field Config through props.\n\n```tsx\nimport React from 'react'\nimport type { TextFieldServerComponent } from 'payload'\n\nexport const MyClientFieldComponent: TextFieldServerComponent = ({ field: { name } }) =\u003e {\n return (\n \u003cp\u003e\n {`This field's name is ${name}`}\n \u003c/p\u003e\n )\n}\n```\n\n### Getting the Current Language\n\nAll Custom Components can support multiple languages to be consistent with Payload's [Internationalization](../configuration/i18n). To do this, first add your translation resources to the [I18n Config](../configuration/i18n).\n\nFrom any Server Component, you can translate resources using the `getTranslation` function from `@payloadcms/translations`. All Server Components automatically receive the `i18n` object as a prop by default.\n\n```tsx\nimport React from 'react'\nimport { getTranslation } from '@payloadcms/translations'\n\nexport default async function MyServerComponent({ i18n }) {\n const translatedTitle = getTranslation(myTranslation, i18n) // highlight-line\n\n return (\n \u003cp\u003e{translatedTitle}\u003c/p\u003e\n )\n}\n```\n\nThe best way to do this within a Client Component is to import the `useTranslation` hook from `@payloadcms/ui`:\n\n```tsx\n'use client'\nimport React from 'react'\nimport { useTranslation } from '@payloadcms/ui'\n\nexport const MyClientComponent: React.FC = () =\u003e {\n const { t, i18n } = useTranslation() // highlight-line\n\n return (\n \u003cul\u003e\n \u003cli\u003e{t('namespace1:key', { variable: 'value' })}\u003c/li\u003e\n \u003cli\u003e{t('namespace2:key', { variable: 'value' })}\u003c/li\u003e\n \u003cli\u003e{i18n.language}\u003c/li\u003e\n \u003c/ul\u003e\n )\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n See the [Hooks](./hooks) documentation for a full list of available hooks.\n\u003c/Banner\u003e\n\n### Getting the Current Locale\n\nAll [Custom Views](./views) can support multiple locales to be consistent with Payload's [Localization](../configuration/localization). They automatically receive the `locale` object as a prop by default. This can be used to scope API requests, etc.:\n\n```tsx\nimport React from 'react'\n\nexport default async function MyServerComponent({ payload, locale }) {\n const localizedPage = await payload.findByID({\n collection: 'pages',\n id: '123',\n locale,\n })\n\n return (\n \u003cp\u003e{localizedPage.title}\u003c/p\u003e\n )\n}\n```\n\nThe best way to do this within a Client Component is to import the `useLocale` hook from `@payloadcms/ui`:\n\n```tsx\n'use client'\nimport React from 'react'\nimport { useLocale } from '@payloadcms/ui'\n\nconst Greeting: React.FC = () =\u003e {\n const locale = useLocale() // highlight-line\n\n const trans = {\n en: 'Hello',\n es: 'Hola',\n }\n\n return (\n \u003cspan\u003e{trans[locale.code]}\u003c/span\u003e\n )\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n See the [Hooks](./hooks) documentation for a full list of available hooks.\n\u003c/Banner\u003e\n\n### Using Hooks\n\nTo make it easier to [build your Custom Components](#building-custom-components), you can use [Payload's built-in React Hooks](./hooks) in any Client Component. For example, you might want to interact with one of Payload's many React Contexts. To do this, you can one of the many hooks available depending on your needs.\n\n```tsx\n'use client'\nimport React from 'react'\nimport { useDocumentInfo } from '@payloadcms/ui'\n\nexport const MyClientComponent: React.FC = () =\u003e {\n const { slug } = useDocumentInfo() // highlight-line\n\n return (\n \u003cp\u003e{`Entity slug: ${slug}`}\u003c/p\u003e\n )\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n See the [Hooks](./hooks) documentation for a full list of available hooks.\n\u003c/Banner\u003e\n\n### Adding Styles\n\nPayload has a robust [CSS Library](./customizing-css) that you can use to style your Custom Components similarly to Payload's built-in styling. This will ensure that your Custom Components match the existing design system, and so that they automatically adapt to any theme changes that might occur.\n\nTo apply custom styles, simply import your own `.css` or `.scss` file into your Custom Component:\n\n```tsx\nimport './index.scss'\n\nexport const MyComponent: React.FC = () =\u003e {\n return (\n \u003cdiv className=\"my-component\"\u003e\n My Custom Component\n \u003c/div\u003e\n )\n}\n```\n\nThen to colorize your Custom Component's background, for example, you can use the following CSS:\n\n```scss\n.my-component {\n background-color: var(--theme-elevation-500);\n}\n```\n\nPayload also exports its [SCSS](https://sass-lang.com) library for reuse which includes mixins, etc. To use this, simply import it as follows into your `.scss` file:\n\n```scss\n@import '~payload/scss';\n\n.my-component {\n @include mid-break {\n background-color: var(--theme-elevation-900);\n }\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n You can also drill into Payload's own component styles, or easily apply global, app-wide CSS. More on that [here](./customizing-css).\n\u003c/Banner\u003e\n\n\n## Root Components\n\nRoot Components are those that effect the [Admin Panel](./overview) generally, such as the logo or the main nav.\n\nTo override Root Components, use the `admin.components` property in your [Payload Config](../getting-started/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n admin: {\n // highlight-start\n components: {\n // ...\n },\n // highlight-end\n },\n})\n```\n\n_For details on how to build Custom Components, see [Building Custom Components](#building-custom-components)._\n\nThe following options are available:\n\n| Path | Description |\n|-----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`Nav`** | Contains the sidebar / mobile menu in its entirety. |\n| **`beforeNavLinks`** | An array of Custom Components to inject into the built-in Nav, _before_ the links themselves. |\n| **`afterNavLinks`** | An array of Custom Components to inject into the built-in Nav, _after_ the links. |\n| **`beforeDashboard`** | An array of Custom Components to inject into the built-in Dashboard, _before_ the default dashboard contents. |\n| **`afterDashboard`** | An array of Custom Components to inject into the built-in Dashboard, _after_ the default dashboard contents. |\n| **`beforeLogin`** | An array of Custom Components to inject into the built-in Login, _before_ the default login form. |\n| **`afterLogin`** | An array of Custom Components to inject into the built-in Login, _after_ the default login form. |\n| **`logout.Button`** | The button displayed in the sidebar that logs the user out. |\n| **`graphics.Icon`** | The simplified logo used in contexts like the the `Nav` component. |\n| **`graphics.Logo`** | The full logo used in contexts like the `Login` view. |\n| **`providers`** | Custom [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context) providers that will wrap the entire Admin Panel. [More details](#custom-providers). |\n| **`actions`** | An array of Custom Components to be rendered _within_ the header of the Admin Panel, providing additional interactivity and functionality. |\n| **`header`** | An array of Custom Components to be injected above the Payload header. |\n| **`views`** | Override or create new views within the Admin Panel. [More details](./views). |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n You can also use set [Collection Components](./collections#custom-components) and [Global Components](./globals#custom-components) in their respective configs.\n\u003c/Banner\u003e\n\n### Custom Providers\n\nAs you add more and more Custom Components to your [Admin Panel](./overview), you may find it helpful to add additional [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context)(s). Payload allows you to inject your own context providers in your app so you can export your own custom hooks, etc.\n\nTo add a Custom Provider, use the `admin.components.providers` property in your [Payload Config](../getting-started/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n admin: {\n components: {\n providers: ['/path/to/MyProvider'], // highlight-line\n },\n },\n})\n```\n\nThen build your Custom Provider as follows:\n\n```tsx\n'use client'\nimport React, { createContext, useContext } from 'react'\n\nconst MyCustomContext = React.createContext(myCustomValue)\n\nexport const MyProvider: React.FC = ({ children }) =\u003e {\n return (\n \u003cMyCustomContext.Provider value={myCustomValue}\u003e\n {children}\n \u003c/MyCustomContext.Provider\u003e\n )\n}\n\nexport const useMyCustomContext = () =\u003e useContext(MyCustomContext)\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003eReact Context exists only within Client Components. This means they must include the `use client` directive at the top of their files and cannot contain server-only code. To use a Server Component here, simply _wrap_ your Client Component with it.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"72:T414a,"])</script><script>self.__next_f.push([1,"\nViews are the individual pages that make up the [Admin Panel](./overview), such as the Dashboard, List, and Edit views. One of the most powerful ways to customize the Admin Panel is to create Custom Views. These are [Custom Components](./components) that can either replace built-in views or can be entirely new.\n\nThere are four types of views within the Admin Panel:\n\n- [Root Views](#root-views)\n- [Collection Views](#collection-views)\n- [Global Views](#global-views)\n- [Document Views](#document-views)\n\nTo swap in your own Custom View, first consult the list of available components, determine the scope that corresponds to what you are trying to accomplish, then [author your React component(s)](#building-custom-views) accordingly.\n\n## Root Views\n\nRoot Views are the main views of the [Admin Panel](./overview). These are views that are scoped directly under the `/admin` route, such as the Dashboard or Account views.\n\nTo swap Root Views with your own, or to [create entirely new ones](#adding-new-root-views), use the `admin.components.views` property of your root [Payload Config](../configuration/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n components: {\n views: {\n customView: {\n Component: '/path/to/MyCustomView#MyCustomView', // highlight-line\n path: '/my-custom-view',\n }\n },\n },\n },\n})\n```\n\nYour Custom Root Views can optionally use one of the templates that Payload provides. The most common of these is the Default Template which provides the basic layout and navigation. Here is an example of what that might look like:\n\n```tsx\nimport type { AdminViewProps } from 'payload'\n\nimport { DefaultTemplate } from '@payloadcms/next/templates'\nimport { Gutter } from '@payloadcms/ui'\nimport React from 'react'\n\nexport const MyCustomView: React.FC\u003cAdminViewProps\u003e = ({\n initPageResult,\n params,\n searchParams,\n}) =\u003e {\n return (\n \u003cDefaultTemplate\n i18n={initPageResult.req.i18n}\n locale={initPageResult.locale}\n params={params}\n payload={initPageResult.req.payload}\n permissions={initPageResult.permissions}\n searchParams={searchParams}\n user={initPageResult.req.user || undefined}\n visibleEntities={initPageResult.visibleEntities}\n \u003e\n \u003cGutter\u003e\n \u003ch1\u003eCustom Default Root View\u003c/h1\u003e\n \u003cbr /\u003e\n \u003cp\u003eThis view uses the Default Template.\u003c/p\u003e\n \u003c/Gutter\u003e\n \u003c/DefaultTemplate\u003e\n )\n}\n```\n\n_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._\n\nThe following options are available:\n\n| Property | Description |\n| --------------- | ----------------------------------------------------------------------------- |\n| **`account`** | The Account view is used to show the currently logged in user's Account page. |\n| **`dashboard`** | The main landing page of the [Admin Panel](./overview). |\n\nFor more granular control, pass a configuration object instead. Payload exposes the following properties for each view:\n\n| Property | Description |\n| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **`Component`** \\* | Pass in the component path that should be rendered when a user navigates to this route. |\n| **`path`** \\* | Any valid URL path or array of paths that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regex) understands. |\n| **`exact`** | Boolean. When true, will only match if the path matches the `usePathname()` exactly. |\n| **`strict`** | When true, a path that has a trailing slash will only match a `location.pathname` with a trailing slash. This has no effect when there are additional URL segments in the pathname. |\n| **`sensitive`** | When true, will match if the path is case sensitive.|\n| **`meta`** | Page metadata overrides to apply to this view within the Admin Panel. [More details](./metadata). |\n\n_\\* An asterisk denotes that a property is required._\n\n### Adding New Views\n\nTo add a _new_ views to the [Admin Panel](./overview), simply add your own key to the `views` object with at least a `path` and `Component` property. For example:\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n components: {\n views: {\n // highlight-start\n myCustomView: {\n // highlight-end\n Component: '/path/to/MyCustomView#MyCustomViewComponent',\n path: '/my-custom-view',\n },\n },\n },\n },\n})\n```\n\nThe above example shows how to add a new [Root View](#root-views), but the pattern is the same for [Collection Views](#collection-views), [Global Views](#global-views), and [Document Views](#document-views). For help on how to build your own Custom Views, see [Building Custom Views](#building-custom-views).\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n Routes are cascading, so unless explicitly given the `exact` property, they will\n match on URLs that simply _start_ with the route's path. This is helpful when creating catch-all\n routes in your application. Alternatively, define your nested route _before_ your parent\n route.\n\u003c/Banner\u003e\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eCustom views are public\u003c/strong\u003e\n \u003cbr /\u003e\n Custom views are public by default. If your view requires a user to be logged in or to have certain access rights, you should handle that within your view component yourself.\n\u003c/Banner\u003e\n\n## Collection Views\n\nCollection Views are views that are scoped under the `/collections` route, such as the Collection List and Document Edit views.\n\nTo swap out Collection Views with your own, or to [create entirely new ones](#adding-new-views), use the `admin.components.views` property of your [Collection Config](../collections/overview):\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollectionConfig: SanitizedCollectionConfig = {\n // ...\n admin: {\n components: {\n views: {\n edit: {\n root: {\n Component: '/path/to/MyCustomEditView', // highlight-line\n }\n // other options include:\n // default\n // versions\n // version\n // api\n // livePreview\n // [key: string]\n // See \"Document Views\" for more details\n },\n list: {\n Component: '/path/to/MyCustomListView',\n }\n },\n },\n },\n}\n```\n\n_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Property | Description |\n| ---------- | ----------------------------------------------------------------------------------------------------------------- |\n| **`edit`** | The Edit View is used to edit a single document for any given Collection. [More details](#document-views). |\n| **`list`** | The List View is used to show a list of documents for any given Collection. |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n You can also add _new_ Collection Views to the config by adding a new key to the `views` object with at least a `path` and `Component` property. See [Adding New Views](#adding-new-views) for more information.\n\u003c/Banner\u003e\n\n## Global Views\n\nGlobal Views are views that are scoped under the `/globals` route, such as the Document Edit View.\n\nTo swap out Global Views with your own or [create entirely new ones](#adding-new-views), use the `admin.components.views` property in your [Global Config](../globals/overview):\n\n```ts\nimport type { SanitizedGlobalConfig } from 'payload'\n\nexport const MyGlobalConfig: SanitizedGlobalConfig = {\n // ...\n admin: {\n components: {\n views: {\n edit: {\n root: {\n Component: '/path/to/MyCustomEditView', // highlight-line\n }\n // other options include:\n // default\n // versions\n // version\n // api\n // livePreview\n // [key: string]\n },\n },\n },\n },\n}\n```\n\n_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Property | Description |\n| ---------- | ------------------------------------------------------------------- |\n| **`edit`** | The Edit View is used to edit a single document for any given Global. [More details](#document-views). |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n You can also add _new_ Global Views to the config by adding a new key to the `views` object with at least a `path` and `Component` property. See [Adding New Views](#adding-new-views) for more information.\n\u003c/Banner\u003e\n\n## Document Views\n\nDocument Views are views that are scoped under the `/collections/:collectionSlug/:id` or the `/globals/:globalSlug` route, such as the Edit View or the API View. All Document Views keep their overall structure across navigation changes, such as their title and tabs, and replace only the content below.\n\nTo swap out Document Views with your own, or to [create entirely new ones](#adding-new-document-views), use the `admin.components.views.Edit[key]` property in your [Collection Config](../collections/overview) or [Global Config](../globals/overview):\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollectionOrGlobalConfig: SanitizedCollectionConfig = {\n // ...\n admin: {\n components: {\n views: {\n edit: {\n api: {\n Component: '/path/to/MyCustomAPIViewComponent', // highlight-line\n },\n },\n },\n },\n },\n}\n```\n\n_For details on how to build Custom Views, including all available props, see [Building Custom Views](#building-custom-views)._\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n If you need to replace the _entire_ Edit View, including _all_ nested Document Views, use the `root` key. See [Custom Collection Views](#collection-views) or [Custom Global Views](#global-views) for more information.\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Property | Description |\n| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |\n| **`root`** | The Root View overrides all other nested views and routes. No document controls or tabs are rendered when this key is set. |\n| **`default`** | The Default View is the primary view in which your document is edited. It is rendered within the \"Edit\" tab. |\n| **`versions`** | The Versions View is used to navigate the version history of a single document. It is rendered within the \"Versions\" tab. [More details](../versions). |\n| **`version`** | The Version View is used to edit a single version of a document. It is rendered within the \"Version\" tab. [More details](../versions). |\n| **`api`** | The API View is used to display the REST API JSON response for a given document. It is rendered within the \"API\" tab. |\n| **`livePreview`** | The LivePreview view is used to display the Live Preview interface. It is rendered within the \"Live Preview\" tab. [More details](../live-preview). |\n\n### Document Tabs\n\nEach Document View can be given a new tab in the Edit View, if desired. Tabs are highly configurable, from as simple as changing the label to swapping out the entire component, they can be modified in any way. To add or customize tabs in the Edit View, use the `tab` key:\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollection: SanitizedCollectionConfig = {\n slug: 'my-collection',\n admin: {\n components: {\n views: {\n edit: {\n myCustomTab: {\n Component: '/path/to/MyCustomTab',\n path: '/my-custom-tab',\n tab: {\n Component: '/path/to/MyCustomTabComponent' // highlight-line\n }\n },\n anotherCustomTab: {\n Component: '/path/to/AnotherCustomView',\n path: '/another-custom-view',\n // highlight-start\n tab: {\n label: 'Another Custom View',\n href: '/another-custom-view',\n }\n // highlight-end\n },\n },\n },\n },\n },\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n This applies to _both_ Collections _and_ Globals.\n\u003c/Banner\u003e\n\n## Building Custom Views\n\nCustom Views are just [Custom Components](./components) rendered at the page-level. To understand how to build Custom Views, first review the [Building Custom Components](./components#building-custom-components) guide. Once you have a Custom Component ready, you can use it as a Custom View.\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollectionConfig: SanitizedCollectionConfig = {\n // ...\n admin: {\n components: {\n views: {\n edit: {\n Component: '/path/to/MyCustomView' // highlight-line\n }\n },\n },\n },\n}\n```\n\n### Default Props\n\nYour Custom Views will be provided with the following props:\n\n| Prop | Description |\n| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| **`initPageResult`** | An object containing `req`, `payload`, `permissions`, etc. |\n| **`clientConfig`** | The Client Config object. [More details](../components#accessing-the-payload-config). |\n| **`importMap`** | The import map object. |\n| **`params`** | An object containing the [Dynamic Route Parameters](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes). |\n| **`searchParams`** | An object containing the [Search Parameters](https://developer.mozilla.org/docs/Learn/Common_questions/What_is_a_URL#parameters). |\n| **`doc`** | The document being edited. Only available in Document Views. [More details](#document-views). |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n All [Custom Server Components](./components) receive `payload` and `i18n` by default. See [Building Custom Components](./components#building-custom-components) for more details.\n\u003c/Banner\u003e\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n It's up to you to secure your custom views. If your view requires a user to be logged in or to\n have certain access rights, you should handle that within your view component yourself.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"73:T604b,"])</script><script>self.__next_f.push([1,"\n[Fields](../fields/overview) within the [Admin Panel](./overview) can be endlessly customized in their appearance and behavior without affecting their underlying data structure. Fields are designed to withstand heavy modification or even complete replacement through the use of [Custom Field Components](#custom-components), [Conditional Logic](#conditional-logic), [Custom Validations](../fields/overview#validation), and more.\n\nFor example, your app might need to render a specific interface that Payload does not inherently support, such as a color picker. To do this, you could replace the default [Text Field](../fields/text) input with your own user-friendly component that formats the data into a valid color value.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n Don't see a built-in field type that you need? Build it! Using a combination of [Field Validations](../fields/overview#validation)\n and [Custom Components](./components), you can override the entirety of how a component functions within the [Admin Panel](./overview) to effectively create your own field type.\n\u003c/Banner\u003e\n\n## Admin Options\n\nYou can customize the appearance and behavior of fields within the [Admin Panel](./overview) through the `admin` property of any [Field Config](../fields/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionConfig: CollectionConfig = {\n // ...\n fields: [\n // ...\n {\n name: 'myField',\n type: 'text',\n admin: { // highlight-line\n // ...\n },\n }\n ]\n}\n```\n\nThe following options are available:\n\n| Option | Description |\n| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`condition`** | Programmatically show / hide fields based on other fields. [More details](../admin/fields#conditional-logic). |\n| **`components`** | All Field Components can be swapped out for [Custom Components](../admin/components) that you define. [More details](../admin/fields). |\n| **`description`** | Helper text to display alongside the field to provide more information for the editor. [More details](../admin/fields#description). |\n| **`position`** | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |\n| **`width`** | Restrict the width of a field. You can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |\n| **`style`** | [CSS Properties](https://developer.mozilla.org/en-US/docs/Web/CSS) to inject into the root element of the field. |\n| **`className`** | Attach a [CSS class attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Class_selectors) to the root DOM element of a field. |\n| **`readOnly`** | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |\n| **`disabled`** | If a field is `disabled`, it is completely omitted from the [Admin Panel](../admin/overview). |\n| **`disableBulkEdit`** | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. Defaults to `true` for UI fields. |\n| **`disableListColumn`** | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |\n| **`disableListFilter`** | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |\n| **`hidden`** | Will transform the field into a `hidden` input type. Its value will still submit with requests in the Admin Panel, but the field itself will not be visible to editors. |\n\n## Field Descriptions\n\nField Descriptions are used to provide additional information to the editor about a field, such as special instructions. Their placement varies from field to field, but typically are displayed with subtle style differences beneath the field inputs.\n\nA description can be configured in three ways:\n\n - As a string.\n - As a function which returns a string. [More details](#description-functions).\n - As a React component. [More details](#description).\n\nTo add a Custom Description to a field, use the `admin.description` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollectionConfig: SanitizedCollectionConfig = {\n // ...\n fields: [\n // ...\n {\n name: 'myField',\n type: 'text',\n admin: {\n description: 'Hello, world!' // highlight-line\n },\n },\n ]\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n To replace the Field Description with a [Custom Component](./components), use the `admin.components.Description` property. [More details](#description).\n\u003c/Banner\u003e\n\n#### Description Functions\n\nCustom Descriptions can also be defined as a function. Description Functions are executed on the server and can be used to format simple descriptions based on the user's current [Locale](../configuration/localization).\n\nTo add a Description Function to a field, set the `admin.description` property to a _function_ in your [Field Config](../fields/overview):\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollectionConfig: SanitizedCollectionConfig = {\n // ...\n fields: [\n // ...\n {\n name: 'myField',\n type: 'text',\n admin: {\n description: ({ t }) =\u003e `${t('Hello, world!')}` // highlight-line\n },\n },\n ]\n}\n```\n\nAll Description Functions receive the following arguments:\n\n| Argument | Description |\n| -------------- | ---------------------------------------------------------------- |\n| **`t`** | The `t` function used to internationalize the Admin Panel. [More details](../configuration/i18n) |\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n If you need to subscribe to live updates within your form, use a Description Component instead. [More details](#description).\n\u003c/Banner\u003e\n\n## Conditional Logic\n\nYou can show and hide fields based on what other fields are doing by utilizing conditional logic on a field by field basis. The `condition` property on a field's admin config accepts a function which takes three arguments:\n\n- `data` - the entire document's data that is currently being edited\n- `siblingData` - only the fields that are direct siblings to the field with the condition\n- `{ user }` - the final argument is an object containing the currently authenticated user\n\nThe `condition` function should return a boolean that will control if the field should be displayed or not.\n\n**Example:**\n\n```ts\n{\n fields: [\n {\n name: 'enableGreeting',\n type: 'checkbox',\n defaultValue: false,\n },\n {\n name: 'greeting',\n type: 'text',\n admin: {\n // highlight-start\n condition: (data, siblingData, { user }) =\u003e {\n if (data.enableGreeting) {\n return true\n } else {\n return false\n }\n },\n // highlight-end\n },\n },\n ]\n}\n```\n\n## Custom Components\n\nWithin the [Admin Panel](./overview), fields are represented in three distinct places:\n\n- [Field](#field) - The actual form field rendered in the Edit View.\n- [Cell](#cell) - The table cell component rendered in the List View.\n- [Filter](#filter) - The filter component rendered in the List View.\n\nTo swap in Field Components with your own, use the `admin.components` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionConfig: CollectionConfig = {\n // ...\n fields: [\n // ...\n {\n // ...\n admin: {\n components: { // highlight-line\n // ...\n },\n },\n }\n ]\n}\n```\n\nThe following options are available:\n\n| Component | Description |\n| ---------- | --------------------------------------------------------------------------------------------------------------------------- |\n| **`Field`** | The form field rendered of the Edit View. [More details](#field). |\n| **`Cell`** | The table cell rendered of the List View. [More details](#cell). |\n| **`Filter`** | The filter component rendered in the List View. [More details](#filter). |\n| **`Label`** | Override the default Label of the Field Component. [More details](#label). |\n| **`Error`** | Override the default Error of the Field Component. [More details](#error). |\n| **`Description`** | Override the default Description of the Field Component. [More details](#description). |\n| **`beforeInput`** | An array of elements that will be added before the input of the Field Component. [More details](#afterinput-and-beforeinput).|\n| **`afterInput`** | An array of elements that will be added after the input of the Field Component. [More details](#afterinput-and-beforeinput). |\n\n### Field\n\nThe Field Component is the actual form field rendered in the Edit View. This is the input that user's will interact with when editing a document.\n\nTo swap in your own Field Component, use the `admin.components.Field` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const CollectionConfig: CollectionConfig = {\n // ...\n fields: [\n // ...\n {\n // ...\n admin: {\n components: {\n Field: '/path/to/MyFieldComponent', // highlight-line\n },\n },\n }\n ]\n}\n```\n\n_For details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components)._\n\n\u003cBanner type=\"warning\"\u003e\n Instead of replacing the entire Field Component, you can alternately replace or slot-in only specific parts by using the [`Label`](#label), [`Error`](#error), [`beforeInput`](#afterinput-and-beforinput), and [`afterInput`](#afterinput-and-beforinput) properties.\n\u003c/Banner\u003e\n\n#### Default Props\n\nAll Field Components receive the following props by default:\n\n| Property | Description |\n| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`docPreferences`** | An object that contains the [Preferences](./preferences) for the document.\n| **`field`** | In Client Components, this is the sanitized Client Field Config. In Server Components, this is the original Field Config. Server Components will also receive the sanitized field config through the`clientField` prop (see below). |\n| **`locale`** | The locale of the field. [More details](../configuration/localization). |\n| **`readOnly`** | A boolean value that represents if the field is read-only or not. |\n| **`user`** | The currently authenticated user. [More details](../authentication/overview). |\n| **`validate`** | A function that can be used to validate the field. |\n| **`path`** | A string representing the direct, dynamic path to the field at runtime, i.e. `myGroup.myArray.0.myField`. |\n| **`schemaPath`** | A string representing the direct, static path to the [Field Config](../fields/overview), i.e. `posts.myGroup.myArray.myField`. |\n| **`indexPath`** | A hyphen-notated string representing the path to the field _within the nearest named ancestor field_, i.e. `0-0` |\n\nIn addition to the above props, all Server Components will also receive the following props:\n\n| Property | Description |\n| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`clientField`** | The serializable Client Field Config. |\n| **`field`** | The Field Config. [More details](../fields/overview). |\n| **`data`** | The current document being edited. |\n| **`i18n`** | The [i18n](../configuration/i18n) object.\n| **`payload`** | The [Payload](../local-api/overview) class. |\n| **`permissions`** | The field permissions based on the currently authenticated user. |\n| **`siblingData`** | The data of the field's siblings. |\n| **`user`** | The currently authenticated user. [More details](../authentication/overview). |\n| **`value`** | The value of the field at render-time. |\n\n#### Sending and receiving values from the form\n\nWhen swapping out the `Field` component, you are responsible for sending and receiving the field's `value` from the form itself.\n\nTo do so, import the [`useField`](./hooks#usefield) hook from `@payloadcms/ui` and use it to manage the field's value:\n\n```tsx\n'use client'\nimport { useField } from '@payloadcms/ui'\n\nexport const CustomTextField: React.FC = () =\u003e {\n const { value, setValue } = useField() // highlight-line\n\n return (\n \u003cinput\n onChange={(e) =\u003e setValue(e.target.value)}\n value={value}\n /\u003e\n )\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n For a complete list of all available React hooks, see the [Payload React Hooks](./hooks) documentation. For additional help, see [Building Custom Components](./components#building-custom-components).\n\u003c/Banner\u003e\n\n#### TypeScript\n\nWhen building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to prepend the field type onto the target type, i.e. `TextFieldClientComponent`:\n\n```tsx\nimport type {\n TextFieldClientComponent,\n TextFieldServerComponent,\n TextFieldClientProps,\n TextFieldServerProps,\n // ...and so on for each Field Type\n} from 'payload'\n```\n\n### Cell\n\nThe Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.\n\nTo swap in your own Cell Component, use the `admin.components.Cell` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const myField: Field = {\n name: 'myField',\n type: 'text',\n admin: {\n components: {\n Cell: '/path/to/MyCustomCellComponent', // highlight-line\n },\n },\n}\n```\n\nAll Cell Components receive the same [Default Field Component Props](#field), plus the following:\n\n| Property | Description |\n| ---------------- | ----------------------------------------------------------------- |\n| **`link`** | A boolean representing whether this cell should be wrapped in a link. |\n| **`onClick`** | A function that is called when the cell is clicked. |\n\nFor details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).\n\n### Filter\n\nThe Filter Component is the actual input element rendered within the \"Filter By\" dropdown of the List View used to represent this field when building filters.\n\nTo swap in your own Filter Component, use the `admin.components.Filter` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const myField: Field = {\n name: 'myField',\n type: 'text',\n admin: {\n components: {\n Filter: '/path/to/MyCustomFilterComponent', // highlight-line\n },\n },\n}\n```\n\nAll Custom Filter Components receive the same [Default Field Component Props](#field).\n\nFor details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).\n\n### Label\n\nThe Label Component is rendered anywhere a field needs to be represented by a label. This is typically used in the Edit View, but can also be used in the List View and elsewhere.\n\nTo swap in your own Label Component, use the `admin.components.Label` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const myField: Field = {\n name: 'myField',\n type: 'text',\n admin: {\n components: {\n Label: '/path/to/MyCustomLabelComponent', // highlight-line\n },\n },\n}\n```\n\nAll Custom Label Components receive the same [Default Field Component Props](#field).\n\nFor details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).\n\n#### TypeScript\n\nWhen building Custom Label Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Label Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `LabelServerComponent` or `LabelClientComponent` to the type of field, i.e. `TextFieldLabelClientComponent`.\n\n```tsx\nimport type {\n TextFieldLabelServerComponent,\n TextFieldLabelClientComponent,\n // ...and so on for each Field Type\n} from 'payload'\n```\n\n### Description\n\nAlternatively to the [Description Property](#the-description-property), you can also use a [Custom Component](./components) as the Field Description. This can be useful when you need to provide more complex feedback to the user, such as rendering dynamic field values or other interactive elements.\n\nTo add a Description Component to a field, use the `admin.components.Description` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollectionConfig: SanitizedCollectionConfig = {\n // ...\n fields: [\n // ...\n {\n name: 'myField',\n type: 'text',\n admin: {\n components: {\n Description: '/path/to/MyCustomDescriptionComponent', // highlight-line\n }\n }\n }\n ]\n}\n```\n\nAll Custom Description Components receive the same [Default Field Component Props](#field).\n\nFor details on how to build a Custom Components themselves, see [Building Custom Components](./components#building-custom-components).\n\n#### TypeScript\n\nWhen building Custom Description Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Description Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `DescriptionServerComponent` or `DescriptionClientComponent` to the type of field, i.e. `TextFieldDescriptionClientComponent`.\n\n```tsx\nimport type {\n TextFieldDescriptionServerComponent,\n TextFieldDescriptionClientComponent,\n // And so on for each Field Type\n} from 'payload'\n```\n\n### Error\n\nThe Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.\n\nTo swap in your own Error Component, use the `admin.components.Error` property in your [Field Config](../fields/overview):\n\n```ts\nimport type { Field } from 'payload'\n\nexport const myField: Field = {\n name: 'myField',\n type: 'text',\n admin: {\n components: {\n Error: '/path/to/MyCustomErrorComponent', // highlight-line\n },\n },\n}\n```\n\nAll Error Components receive the [Default Field Component Props](#field).\n\nFor details on how to build Custom Components themselves, see [Building Custom Components](./components#building-custom-components).\n\n#### TypeScript\n\nWhen building Custom Error Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Error Component, one for every [Field Type](../fields/overview) and server/client environment. The convention is to append `ErrorServerComponent` or `ErrorClientComponent` to the type of field, i.e. `TextFieldErrorClientComponent`.\n\n```tsx\nimport type {\n TextFieldErrorServerComponent,\n TextFieldErrorClientComponent,\n // And so on for each Field Type\n} from 'payload'\n```\n\n### afterInput and beforeInput\n\nWith these properties you can add multiple components _before_ and _after_ the input element, as their name suggests. This is useful when you need to render additional elements alongside the field without replacing the entire field component.\n\nTo add components before and after the input element, use the `admin.components.beforeInput` and `admin.components.afterInput` properties in your [Field Config](../fields/overview):\n\n```ts\nimport type { SanitizedCollectionConfig } from 'payload'\n\nexport const MyCollectionConfig: SanitizedCollectionConfig = {\n // ...\n fields: [\n // ...\n {\n name: 'myField',\n type: 'text',\n admin: {\n components: {\n // highlight-start\n beforeInput: ['/path/to/MyCustomComponent'],\n afterInput: ['/path/to/MyOtherCustomComponent'],\n // highlight-end\n }\n }\n }\n ]\n}\n```\n\nAll `afterInput` and `beforeInput` Components receive the same [Default Field Component Props](#field).\n\nFor details on how to build Custom Components, see [Building Custom Components](./components#building-custom-components).\n"])</script><script>self.__next_f.push([1,"74:T6b52,"])</script><script>self.__next_f.push([1,"\nPayload provides a variety of powerful [React Hooks](https://react.dev/reference/react-dom/hooks) that can be used within your own [Custom Components](./components), such as [Custom Fields](./fields). With them, you can interface with Payload itself to build just about any type of complex customization you can think of.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n All Custom Components are [React Server Components](https://react.dev/reference/rsc/server-components) by default. Hooks, on the other hand, are only available in client-side environments. To use hooks, [ensure your component is a client component](./components#client-components).\n\u003c/Banner\u003e\n\n## useField\n\nThe `useField` hook is used internally within all field components. It manages sending and receiving a field's state from its parent form. When you build a [Custom Field Component](./fields), you will be responsible for sending and receiving the field's `value` to and from the form yourself.\n\nTo do so, import the `useField` hook as follows:\n\n```tsx\n'use client'\nimport type { TextFieldClientComponent } from 'payload'\nimport { useField } from '@payloadcms/ui'\n\nexport const CustomTextField: TextFieldClientComponent = ({ path }) =\u003e {\n const { value, setValue } = useField({ path }) // highlight-line\n\n return (\n \u003cdiv\u003e\n \u003cp\u003e\n {path}\n \u003c/p\u003e\n \u003cinput\n onChange={(e) =\u003e { setValue(e.target.value) }}\n value={value}\n /\u003e\n \u003c/div\u003e\n )\n}\n```\n\nThe `useField` hook accepts the following arguments:\n\n| Property | Description |\n| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `path` | If you do not provide a `path`, `name` will be used instead. This is the path to the field in the form data. |\n| `validate` | A validation function executed client-side _before_ submitting the form to the server. Different than [Field-level Validation](../fields/overview#validation) which runs strictly on the server. |\n| `disableFormData` | If `true`, the field will not be included in the form data when the form is submitted. |\n| `hasRows` | If `true`, the field will be treated as a field with rows. This is useful for fields like `array` and `blocks`. |\n\nThe `useField` hook returns the following object:\n\n```ts\ntype FieldType\u003cT\u003e = {\n errorMessage?: string\n errorPaths?: string[]\n filterOptions?: FilterOptionsResult\n formInitializing: boolean\n formProcessing: boolean\n formSubmitted: boolean\n initialValue?: T\n path: string\n permissions: FieldPermissions\n readOnly?: boolean\n rows?: Row[]\n schemaPath: string\n setValue: (val: unknown, disableModifyingForm?: boolean) =\u003e void\n showError: boolean\n valid?: boolean\n value: T\n}\n```\n\n## useFormFields\n\nThere are times when a custom field component needs to have access to data from other fields, and you have a few options to do so. The `useFormFields` hook is a powerful and highly performant way to retrieve a form's field state, as well as to retrieve the `dispatchFields` method, which can be helpful for setting other fields' form states from anywhere within a form.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eThis hook is great for retrieving only certain fields from form state\u003c/strong\u003e because it\n ensures that it will only cause a rerender when the items that you ask for change.\n\u003c/Banner\u003e\n\nThanks to the awesome package [`use-context-selector`](https://github.com/dai-shi/use-context-selector), you can retrieve a specific field's state easily. This is ideal because you can ensure you have an up-to-date field state, and your component will only re-render when _that field's state_ changes.\n\nYou can pass a Redux-like selector into the hook, which will ensure that you retrieve only the field that you want. The selector takes an argument with type of `[fields: Fields, dispatch: React.Dispatch\u003cAction\u003e]]`.\n\n```tsx\n'use client'\nimport { useFormFields } from '@payloadcms/ui'\n\nconst MyComponent: React.FC = () =\u003e {\n // Get only the `amount` field state, and only cause a rerender when that field changes\n const amount = useFormFields(([fields, dispatch]) =\u003e fields.amount)\n\n // Do the same thing as above, but to the `feePercentage` field\n const feePercentage = useFormFields(([fields, dispatch]) =\u003e fields.feePercentage)\n\n if (typeof amount?.value !== 'undefined' \u0026\u0026 typeof feePercentage?.value !== 'undefined') {\n return \u003cspan\u003eThe fee is ${(amount.value * feePercentage.value) / 100}\u003c/span\u003e\n }\n}\n```\n\n## useAllFormFields\n\n**To retrieve more than one field**, you can use the `useAllFormFields` hook. Your component will re-render when _any_ field changes, so use this hook only if you absolutely need to. Unlike the `useFormFields` hook, this hook does not accept a \"selector\", and it always returns an array with type of `[fields: Fields, dispatch: React.Dispatch\u003cAction\u003e]]`.\n\nYou can do lots of powerful stuff by retrieving the full form state, like using built-in helper functions to reduce field state to values only, or to retrieve sibling data by path.\n\n```tsx\n'use client'\nimport { useAllFormFields } from '@payloadcms/ui'\nimport { reduceFieldsToValues, getSiblingData } from 'payload/shared'\n\nconst ExampleComponent: React.FC = () =\u003e {\n // the `fields` const will be equal to all fields' state,\n // and the `dispatchFields` method is usable to send field state up to the form\n const [fields, dispatchFields] = useAllFormFields();\n\n // Pass in fields, and indicate if you'd like to \"unflatten\" field data.\n // The result below will reflect the data stored in the form at the given time\n const formData = reduceFieldsToValues(fields, true);\n\n // Pass in field state and a path,\n // and you will be sent all sibling data of the path that you've specified\n const siblingData = getSiblingData(fields, 'someFieldName');\n\n return (\n // return some JSX here if necessary\n )\n};\n```\n\n#### Updating other fields' values\n\nIf you are building a Custom Component, then you should use `setValue` which is returned from the `useField` hook to programmatically set your field's value. But if you're looking to update _another_ field's value, you can use `dispatchFields` returned from `useFormFields`.\n\nYou can send the following actions to the `dispatchFields` function.\n\n| Action | Description |\n| ---------------------- | -------------------------------------------------------------------------- |\n| **`ADD_ROW`** | Adds a row of data (useful in array / block field data) |\n| **`DUPLICATE_ROW`** | Duplicates a row of data (useful in array / block field data) |\n| **`MODIFY_CONDITION`** | Updates a field's conditional logic result (true / false) |\n| **`MOVE_ROW`** | Moves a row of data (useful in array / block field data) |\n| **`REMOVE`** | Removes a field from form state |\n| **`REMOVE_ROW`** | Removes a row of data from form state (useful in array / block field data) |\n| **`REPLACE_STATE`** | Completely replaces form state |\n| **`UPDATE`** | Update any property of a specific field's state |\n\nTo see types for each action supported within the `dispatchFields` hook, check out the Form types [here](https://github.com/payloadcms/payload/blob/main/packages/payload/src/admin/components/forms/Form/types.ts).\n\n## useForm\n\nThe `useForm` hook can be used to interact with the form itself, and sends back many methods that can be used to reactively fetch form state without causing rerenders within your components each time a field is changed. This is useful if you have action-based callbacks that your components fire, and need to interact with form state _based on a user action_.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eWarning:\u003c/strong\u003e\n \u003cbr /\u003e\n This hook is optimized to avoid causing rerenders when fields change, and as such, its `fields`\n property will be out of date. You should only leverage this hook if you need to perform actions\n against the form in response to your users' actions. Do not rely on its returned \"fields\" as being\n up-to-date. They will be removed from this hook's response in an upcoming version.\n\u003c/Banner\u003e\n\nThe `useForm` hook returns an object with the following properties:\n\n\u003cTableWithDrawers\n columns={[\n 'Action',\n 'Description',\n 'Example',\n ]}\n rows={[\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003efields\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"Deprecated. This property cannot be relied on as up-to-date.\",\n },\n {\n value: ''\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003esubmit\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"Method to trigger the form to submit\",\n },\n {\n value: ''\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003edispatchFields\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"Dispatch actions to the form field state\",\n },\n {\n value: ''\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003evalidateForm\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"Trigger a validation of the form state\",\n },\n {\n value: ''\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003ecreateFormData\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \u003c\u003eCreate a \u003ccode\u003emultipart/form-data\u003c/code\u003e object from the current form's state\u003c/\u003e,\n },\n {\n value: ''\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003edisabled\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"Boolean denoting whether or not the form is disabled\",\n },\n {\n value: ''\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003egetFields\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: 'Gets all fields from state',\n },\n {\n value: '',\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003egetField\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: 'Gets a single field from state by path',\n },\n {\n value: '',\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003egetData\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: 'Returns the data stored in the form',\n },\n {\n value: '',\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003egetSiblingData\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: 'Returns form sibling data for the given field path',\n },\n {\n value: '',\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003esetModified\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \u003c\u003eSet the form\\'s \u003ccode\u003emodified\u003c/code\u003e state\u003c/\u003e,\n },\n {\n value: '',\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003esetProcessing\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \u003c\u003eSet the form\\'s \u003ccode\u003eprocessing\u003c/code\u003e state\u003c/\u003e,\n },\n {\n value: '',\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003esetSubmitted\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \u003c\u003eSet the form\\'s \u003ccode\u003esubmitted\u003c/code\u003e state\u003c/\u003e,\n },\n {\n value: '',\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003eformRef\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: 'The ref from the form HTML element',\n },\n {\n value: '',\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003ereset\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: 'Method to reset the form to its initial state',\n },\n {\n value: '',\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003eaddFieldRow\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"Method to add a row on an array or block field\",\n },\n {\n drawerTitle: 'addFieldRow',\n drawerDescription: 'A useful method to programmatically add a row to an array or block field.',\n drawerSlug: 'addFieldRow',\n drawerContent: (\n\u003c\u003e\n \u003cTableWithDrawers\n columns={[\n 'Prop',\n 'Description',\n ]}\n rows={[\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003epath\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"The path to the array or block field\",\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003erowIndex\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"The index of the row to add. If omitted, the row will be added to the end of the array.\",\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003edata\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"The data to add to the row\",\n },\n ],\n ]}\n /\u003e\n\n{' '}\n\n\u003cbr /\u003e\n\n{' '}\n\n\u003cpre\u003e\n {`import { useForm } from \"payload/components/forms\";\n\nexport const CustomArrayManager = () =\u003e {\n const { addFieldRow } = useForm()\n\n return (\n \u003cbutton\n type=\"button\"\n onClick={() =\u003e {\n addFieldRow({\n path: \"arrayField\",\n rowIndex: 0,\n data: {\n textField: \"text\",\n // blockType: \"yourBlockSlug\",\n // ^ if managing a block array, you need to specify the block type\n },\n })\n }}\n \u003e\n Add Row\n \u003c/button\u003e\n )\n}`}\n\u003c/pre\u003e\n\n \u003cp\u003eAn example config to go along with the Custom Component\u003c/p\u003e\n \u003cpre\u003e\n{`const ExampleCollection = {\n slug: \"example-collection\",\n fields: [\n {\n name: \"arrayField\",\n type: \"array\",\n fields: [\n {\n name: \"textField\",\n type: \"text\",\n },\n ],\n },\n {\n type: \"ui\",\n name: \"customArrayManager\",\n admin: {\n components: {\n Field: '/path/to/CustomArrayManagerField',\n },\n },\n },\n ],\n}`}\n \u003c/pre\u003e\n\u003c/\u003e\n )\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003eremoveFieldRow\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"Method to remove a row from an array or block field\",\n },\n {\n drawerTitle: 'removeFieldRow',\n drawerDescription: 'A useful method to programmatically remove a row from an array or block field.',\n drawerSlug: 'removeFieldRow',\n drawerContent: (\n\u003c\u003e\n \u003cTableWithDrawers\n columns={[\n 'Prop',\n 'Description',\n ]}\n rows={[\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003epath\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"The path to the array or block field\",\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003erowIndex\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"The index of the row to remove\",\n },\n ],\n ]}\n /\u003e\n\n{' '}\n\n\u003cbr /\u003e\n\n{' '}\n\n\u003cpre\u003e\n {`import { useForm } from \"payload/components/forms\";\n\nexport const CustomArrayManager = () =\u003e {\n const { removeFieldRow } = useForm()\n\n return (\n \u003cbutton\n type=\"button\"\n onClick={() =\u003e {\n removeFieldRow({\n path: \"arrayField\",\n rowIndex: 0,\n })\n }}\n \u003e\n Remove Row\n \u003c/button\u003e\n )\n}`}\n\u003c/pre\u003e\n\n \u003cp\u003eAn example config to go along with the Custom Component\u003c/p\u003e\n \u003cpre\u003e\n{`const ExampleCollection = {\n slug: \"example-collection\",\n fields: [\n {\n name: \"arrayField\",\n type: \"array\",\n fields: [\n {\n name: \"textField\",\n type: \"text\",\n },\n ],\n },\n {\n type: \"ui\",\n name: \"customArrayManager\",\n admin: {\n components: {\n Field: '/path/to/CustomArrayManagerField',\n },\n },\n },\n ],\n}`}\n \u003c/pre\u003e\n\u003c/\u003e\n )\n }\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003ereplaceFieldRow\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"Method to replace a row from an array or block field\",\n },\n {\n drawerTitle: 'replaceFieldRow',\n drawerDescription: 'A useful method to programmatically replace a row from an array or block field.',\n drawerSlug: 'replaceFieldRow',\n drawerContent: (\n\u003c\u003e\n \u003cTableWithDrawers\n columns={[\n 'Prop',\n 'Description',\n ]}\n rows={[\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003epath\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"The path to the array or block field\",\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003erowIndex\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"The index of the row to replace\",\n },\n ],\n [\n {\n value: \u003cstrong\u003e\u003ccode\u003edata\u003c/code\u003e\u003c/strong\u003e,\n },\n {\n value: \"The data to replace within the row\",\n },\n ],\n ]}\n /\u003e\n\n{' '}\n\n\u003cbr /\u003e\n\n{' '}\n\n\u003cpre\u003e\n {`import { useForm } from \"payload/components/forms\";\n\nexport const CustomArrayManager = () =\u003e {\n const { replaceFieldRow } = useForm()\n\n return (\n \u003cbutton\n type=\"button\"\n onClick={() =\u003e {\n replaceFieldRow({\n path: \"arrayField\",\n rowIndex: 0,\n data: {\n textField: \"updated text\",\n // blockType: \"yourBlockSlug\",\n // ^ if managing a block array, you need to specify the block type\n },\n })\n }}\n \u003e\n Replace Row\n \u003c/button\u003e\n )\n}`}\n\u003c/pre\u003e\n\n \u003cp\u003eAn example config to go along with the Custom Component\u003c/p\u003e\n \u003cpre\u003e\n{`const ExampleCollection = {\n slug: \"example-collection\",\n fields: [\n {\n name: \"arrayField\",\n type: \"array\",\n fields: [\n {\n name: \"textField\",\n type: \"text\",\n },\n ],\n },\n {\n type: \"ui\",\n name: \"customArrayManager\",\n admin: {\n components: {\n Field: '/path/to/CustomArrayManagerField',\n },\n },\n },\n ],\n}`}\n \u003c/pre\u003e\n\u003c/\u003e\n )\n }\n ],\n ]}\n/\u003e\n\n## useCollapsible\n\nThe `useCollapsible` hook allows you to control parent collapsibles:\n\n| Property | Description |\n| ------------------------- | ------------------------------------------------------------------------------------------------------------- |\n| **`isCollapsed`** | State of the collapsible. `true` if open, `false` if collapsed. |\n| **`isVisible`** | If nested, determine if the nearest collapsible is visible. `true` if no parent is closed, `false` otherwise. |\n| **`toggle`** | Toggles the state of the nearest collapsible. |\n| **`isWithinCollapsible`** | Determine when you are within another collapsible. |\n\n**Example:**\n\n```tsx\n'use client'\nimport React from 'react'\n\nimport { useCollapsible } from '@payloadcms/ui'\n\nconst CustomComponent: React.FC = () =\u003e {\n const { isCollapsed, toggle } = useCollapsible()\n\n return (\n \u003cdiv\u003e\n \u003cp className=\"field-type\"\u003eI am {isCollapsed ? 'closed' : 'open'}\u003c/p\u003e\n \u003cbutton onClick={toggle} type=\"button\"\u003e\n Toggle\n \u003c/button\u003e\n \u003c/div\u003e\n )\n}\n```\n\n## useDocumentInfo\n\nThe `useDocumentInfo` hook provides lots of information about the document currently being edited, including the following:\n\n| Property | Description |\n| ------------------------- | ------------------------------------------------------------------------------------------------------------------ |\n| **`collection`** | If the doc is a collection, its Collection Config will be returned |\n| **`global`** | If the doc is a global, its Global Config will be returned |\n| **`id`** | If the doc is a collection, its ID will be returned |\n| **`preferencesKey`** | The `preferences` key to use when interacting with document-level user preferences |\n| **`versions`** | Versions of the current doc |\n| **`unpublishedVersions`** | Unpublished versions of the current doc |\n| **`publishedDoc`** | The currently published version of the doc being edited |\n| **`getVersions`** | Method to trigger the retrieval of document versions |\n| **`docPermissions`** | The current documents permissions. Collection document permissions fallback when no id is present (i.e. on create) |\n| **`getDocPermissions`** | Method to trigger the retrieval of document level permissions |\n\n**Example:**\n\n```tsx\n'use client'\nimport { useDocumentInfo } from '@payloadcms/ui'\n\nconst LinkFromCategoryToPosts: React.FC = () =\u003e {\n // highlight-start\n const { id } = useDocumentInfo()\n // highlight-end\n\n // id will be undefined on the create form\n if (!id) {\n return null\n }\n\n return (\n \u003ca href={`/admin/collections/posts?where[or][0][and][0][category][in][0]=[${id}]`}\u003e\n View posts\n \u003c/a\u003e\n )\n}\n```\n\n## useLocale\n\nIn any Custom Component you can get the selected locale object with the `useLocale` hook. `useLocale` gives you the full locale object, consisting of a `label`, `rtl`(right-to-left) property, and then `code`. Here is a simple example:\n\n```tsx\n'use client'\nimport { useLocale } from '@payloadcms/ui'\n\nconst Greeting: React.FC = () =\u003e {\n // highlight-start\n const locale = useLocale()\n // highlight-end\n\n const trans = {\n en: 'Hello',\n es: 'Hola',\n }\n\n return \u003cspan\u003e {trans[locale.code]} \u003c/span\u003e\n}\n```\n\n## useAuth\n\nUseful to retrieve info about the currently logged in user as well as methods for interacting with it. It sends back an object with the following properties:\n\n| Property | Description |\n| ------------------------ | --------------------------------------------------------------------------------------- |\n| **`user`** | The currently logged in user |\n| **`logOut`** | A method to log out the currently logged in user |\n| **`refreshCookie`** | A method to trigger the silent refreshing of a user's auth token |\n| **`setToken`** | Set the token of the user, to be decoded and used to reset the user and token in memory |\n| **`token`** | The logged in user's token (useful for creating preview links, etc.) |\n| **`refreshPermissions`** | Load new permissions (useful when content that effects permissions has been changed) |\n| **`permissions`** | The permissions of the current user |\n\n```tsx\n'use client'\nimport { useAuth } from '@payloadcms/ui'\nimport type { User } from '../payload-types.ts'\n\nconst Greeting: React.FC = () =\u003e {\n // highlight-start\n const { user } = useAuth\u003cUser\u003e()\n // highlight-end\n\n return \u003cspan\u003eHi, {user.email}!\u003c/span\u003e\n}\n```\n\n## useConfig\n\nUsed to retrieve the Payload [Client Config](./components#accessing-the-payload-config).\n\n```tsx\n'use client'\nimport { useConfig } from '@payloadcms/ui'\n\nconst MyComponent: React.FC = () =\u003e {\n // highlight-start\n const { config } = useConfig()\n // highlight-end\n\n return \u003cspan\u003e{config.serverURL}\u003c/span\u003e\n}\n```\n\n## useEditDepth\n\nSends back how many editing levels \"deep\" the current component is. Edit depth is relevant while adding new documents / editing documents in modal windows and other cases.\n\n```tsx\n'use client'\nimport { useEditDepth } from '@payloadcms/ui'\n\nconst MyComponent: React.FC = () =\u003e {\n // highlight-start\n const editDepth = useEditDepth()\n // highlight-end\n\n return \u003cspan\u003eMy component is {editDepth} levels deep\u003c/span\u003e\n}\n```\n\n## usePreferences\n\nReturns methods to set and get user preferences. More info can be found [here](../admin/preferences).\n\n## useTheme\n\nReturns the currently selected theme (`light`, `dark` or `auto`), a set function to update it and a boolean `autoMode`, used to determine if the theme value should be set automatically based on the user's device preferences.\n\n```tsx\n'use client'\nimport { useTheme } from '@payloadcms/ui'\n\nconst MyComponent: React.FC = () =\u003e {\n // highlight-start\n const { autoMode, setTheme, theme } = useTheme()\n // highlight-end\n\n return (\n \u003c\u003e\n \u003cspan\u003e\n The current theme is {theme} and autoMode is {autoMode}\n \u003c/span\u003e\n \u003cbutton\n type=\"button\"\n onClick={() =\u003e setTheme((prev) =\u003e (prev === 'light' ? 'dark' : 'light'))}\n \u003e\n Toggle theme\n \u003c/button\u003e\n \u003c/\u003e\n )\n}\n```\n\n## useTableColumns\n\nReturns methods to manipulate table columns\n\n```tsx\n'use client'\nimport { useTableColumns } from '@payloadcms/ui'\n\nconst MyComponent: React.FC = () =\u003e {\n // highlight-start\n const { setActiveColumns } = useTableColumns()\n\n const resetColumns = () =\u003e {\n setActiveColumns(['id', 'createdAt', 'updatedAt'])\n }\n // highlight-end\n\n return (\n \u003cbutton type=\"button\" onClick={resetColumns}\u003e\n Reset columns\n \u003c/button\u003e\n )\n}\n```\n\n## useDocumentEvents\n\nThe `useDocumentEvents` hook provides a way of subscribing to cross-document events, such as updates made to nested documents within a drawer. This hook will report document events that are outside the scope of the document currently being edited. This hook provides the following:\n\n| Property | Description |\n| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |\n| **`mostRecentUpdate`** | An object containing the most recently updated document. It contains the `entitySlug`, `id` (if collection), and `updatedAt` properties |\n| **`reportUpdate`** | A method used to report updates to documents. It accepts the same arguments as the `mostRecentUpdate` property. |\n\n**Example:**\n\n```tsx\n'use client'\nimport { useDocumentEvents } from '@payloadcms/ui'\n\nconst ListenForUpdates: React.FC = () =\u003e {\n const { mostRecentUpdate } = useDocumentEvents()\n\n return \u003cspan\u003e{JSON.stringify(mostRecentUpdate)}\u003c/span\u003e\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n Right now the `useDocumentEvents` hook only tracks recently updated documents, but in the future\n it will track more document-related events as needed, such as document creation, deletion, etc.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"75:T1fc5,"])</script><script>self.__next_f.push([1,"\nEvery page within the Admin Panel automatically receives dynamic, auto-generated metadata derived from live document data, the user's current locale, and more, without any additional configuration. This includes the page title, description, og:image and everything in between. Metadata is fully configurable at the root level and cascades down to individual collections, documents, and custom views, allowing for the ability to control metadata on any page with high precision.\n\nWithin the Admin Panel, metadata can be customized at the following levels:\n\n- [Root Metadata](#root-metadata)\n- [Collection Metadata](#collection-metadata)\n- [Global Metadata](#global-metadata)\n- [View Metadata](#view-metadata)\n\nAll of these types of metadata share a similar structure, with a few key differences on the Root level. To customize metadata, consult the list of available scopes. Determine the scope that corresponds to what you are trying to accomplish, then author your metadata within the Payload Config accordingly.\n\n## Root Metadata\n\nRoot Metadata is the metadata that is applied to all pages within the Admin Panel. This is where you can control things like the suffix appended onto each page's title, the favicon displayed in the browser's tab, and the Open Graph data that is used when sharing the Admin Panel on social media.\n\nTo customize Root Metadata, use the `admin.meta` key in your Payload Config:\n\n```ts\n{\n // ...\n admin: {\n // highlight-start\n meta: {\n // highlight-end\n title: 'My Admin Panel',\n description: 'The best admin panel in the world',\n icons: [\n {\n rel: 'icon',\n type: 'image/png',\n url: '/favicon.png',\n },\n ],\n },\n },\n}\n```\n\nThe following options are available for Root Metadata:\n\n| Key | Type | Description |\n| --- | --- | --- |\n| **`title`** | `string` | The title of the Admin Panel. |\n| **`description`** | `string` | The description of the Admin Panel. |\n| **`defaultOGImageType`** | `dynamic` (default), `static`, or `off` | The type of default OG image to use. If set to `dynamic`, Payload will use Next.js image generation to create an image with the title of the page. If set to `static`, Payload will use the `defaultOGImage` URL. If set to `off`, Payload will not generate an OG image. |\n| **`icons`** | `IconConfig[]` | An array of icon objects. [More details](#icons) |\n| **`keywords`** | `string` | A comma-separated list of keywords to include in the metadata of the Admin Panel. |\n| **`openGraph`** | `OpenGraphConfig` | An object containing Open Graph metadata. [More details](#open-graph) |\n| **`titleSuffix`** | `string` | A suffix to append to the end of the title of every page. Defaults to \"- Payload\". |\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n These are the _root-level_ options for the Admin Panel. You can also customize [Collection Metadata](./collections), [Global Metadata](./globals), and [Document Metadata](./documents) in their respective configs.\n\u003c/Banner\u003e\n\n### Icons\n\nThe Icons Config corresponds to the `\u003clink\u003e` tags that are used to specify icons for the Admin Panel. The `icons` key is an array of objects, each of which represents an individual icon. Icons are differentiated from one another by their `rel` attribute, which specifies the relationship between the document and the icon.\n\nThe most common icon type is the favicon, which is displayed in the browser tab. This is specified by the `rel` attribute `icon`. Other common icon types include `apple-touch-icon`, which is used by Apple devices when the Admin Panel is saved to the home screen, and `mask-icon`, which is used by Safari to mask the Admin Panel icon.\n\nTo customize icons, use the `icons` key within the `admin.meta` object in your Payload Config:\n\n```ts\n{\n // ...\n admin: {\n meta: {\n // highlight-start\n icons: [\n // highlight-end\n {\n rel: 'icon',\n type: 'image/png',\n url: '/favicon.png',\n },\n {\n rel: 'apple-touch-icon',\n type: 'image/png',\n url: '/apple-touch-icon.png',\n },\n ],\n },\n },\n}\n```\n\nThe following options are available for Icons:\n\n| Key | Type | Description |\n| --- | --- | --- |\n| **`rel`** | `string` | The HTML `rel` attribute of the icon. |\n| **`type`** | `string` | The MIME type of the icon. |\n| **`color`** | `string` | The color of the icon. |\n| **`fetchPriority`** | `string` | The [fetch priority](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/fetchPriority) of the icon. |\n| **`media`** | `string` | The [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries) of the icon. |\n| **`sizes`** | `string` | The [sizes](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/sizes) of the icon. |\n| **`url`** | `string` | The URL pointing the resource of the icon. |\n\n### Open Graph\n\nOpen Graph metadata is a set of tags that are used to control how URLs are displayed when shared on social media platforms. Open Graph metadata is automatically generated by Payload, but can be customized at the Root level.\n\nTo customize Open Graph metadata, use the `openGraph` key within the `admin.meta` object in your Payload Config:\n\n```ts\n{\n // ...\n admin: {\n meta: {\n // highlight-start\n openGraph: {\n // highlight-end\n description: 'The best admin panel in the world',\n images: [\n {\n url: 'https://example.com/image.jpg',\n width: 800,\n height: 600,\n },\n ],\n siteName: 'Payload',\n title: 'My Admin Panel',\n },\n },\n },\n}\n```\n\nThe following options are available for Open Graph Metadata:\n\n| Key | Type | Description |\n| --- | --- | --- |\n| **`description`** | `string` | The description of the Admin Panel. |\n| **`images`** | `OGImageConfig` or `OGImageConfig[]` | An array of image objects. |\n| **`siteName`** | `string` | The name of the site. |\n| **`title`** | `string` | The title of the Admin Panel. |\n\n## Collection Metadata\n\nCollection Metadata is the metadata that is applied to all pages within any given Collection within the Admin Panel. This metadata is used to customize the title and description of all views within any given Collection, unless overridden by the view itself.\n\nTo customize Collection Metadata, use the `admin.meta` key within your Collection Config:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const MyCollection: CollectionConfig = {\n // ...\n admin: {\n // highlight-start\n meta: {\n // highlight-end\n title: 'My Collection',\n description: 'The best collection in the world',\n },\n },\n}\n```\n\nThe Collection Meta config has the same options as the [Root Metadata](#root-metadata) config.\n\n## Global Metadata\n\nGlobal Metadata is the metadata that is applied to all pages within any given Global within the Admin Panel. This metadata is used to customize the title and description of all views within any given Global, unless overridden by the view itself.\n\nTo customize Global Metadata, use the `admin.meta` key within your Global Config:\n\n```ts\nimport { GlobalConfig } from 'payload'\n\nexport const MyGlobal: GlobalConfig = {\n // ...\n admin: {\n // highlight-start\n meta: {\n // highlight-end\n title: 'My Global',\n description: 'The best\n },\n },\n}\n```\n\nThe Global Meta config has the same options as the [Root Metadata](#root-metadata) config.\n\n## View Metadata\n\nView Metadata is the metadata that is applied to specific [Views](./views) within the Admin Panel. This metadata is used to customize the title and description of a specific view, overriding any metadata set at the [Root](#root-metadata), [Collection](#collection-metadata), or [Global](#global-metadata) level.\n\nTo customize View Metadata, use the `meta` key within your View Config:\n\n```ts\n{\n // ...\n admin: {\n views: {\n dashboard: {\n // highlight-start\n meta: {\n // highlight-end\n title: 'My Dashboard',\n description: 'The best dashboard in the world',\n }\n },\n },\n },\n}\n"])</script><script>self.__next_f.push([1,"76:T18e9,"])</script><script>self.__next_f.push([1,"\nAs your users interact with the [Admin Panel](./overview), you might want to store their preferences in a persistent manner, so that when they revisit the Admin Panel in a different session or from a different device, they can pick right back up where they left off.\n\nOut of the box, Payload handles the persistence of your users' preferences in a handful of ways, including:\n\n1. Columns in the Collection List View: their active state and order\n1. The user's last active [Locale](../configuration/localization)\n1. The \"collapsed\" state of `blocks`, `array`, and `collapsible` fields\n1. The last-known state of the `Nav` component, etc.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n \u003cbr /\u003e\n All preferences are stored on an individual user basis. Payload automatically recognizes the user\n that is reading or setting a preference via all provided authentication methods.\n\u003c/Banner\u003e\n\n## Use Cases\n\nThis API is used significantly for internal operations of the Admin Panel, as mentioned above. But, if you're building your own React components for use in the Admin Panel, you can allow users to set their own preferences in correspondence to their usage of your components. For example:\n\n- If you have built a \"color picker\", you could \"remember\" the last used colors that the user has set for easy access next time\n- If you've built a custom `Nav` component, and you've built in an \"accordion-style\" UI, you might want to store the `collapsed` state of each Nav collapsible item. This way, if an editor returns to the panel, their `Nav` state is persisted automatically\n- You might want to store `recentlyAccessed` documents to give admin editors an easy shortcut back to their recently accessed documents on the `Dashboard` or similar\n- Many other use cases exist. Invent your own! Give your editors an intelligent and persistent editing experience.\n\n## Database\n\nPayload automatically creates an internally used `payload-preferences` Collection that stores user preferences. Each document in the `payload-preferences` Collection contains the following shape:\n\n| Key | Value |\n| ----------------- | ----------------------------------------------------------------- |\n| `id` | A unique ID for each preference stored. |\n| `key` | A unique `key` that corresponds to the preference. |\n| `user.value` | The ID of the `user` that is storing its preference. |\n| `user.relationTo` | The `slug` of the Collection that the `user` is logged in as. |\n| `value` | The value of the preference. Can be any data shape that you need. |\n| `createdAt` | A timestamp of when the preference was created. |\n| `updatedAt` | A timestamp set to the last time the preference was updated. |\n\n## APIs\n\nPreferences are available to both [GraphQL](../graphql/overview#preferences) and [REST](../rest-api/overview#preferences) APIs.\n\n## Adding or reading Preferences in your own components\n\nThe Payload Admin Panel offers a `usePreferences` hook. The hook is only meant for use within the Admin Panel itself. It provides you with two methods:\n\n#### `getPreference`\n\nThis async method provides an easy way to retrieve a user's preferences by `key`. It will return a promise containing the resulting preference value.\n\n**Arguments**\n\n- `key`: the `key` of your preference to retrieve.\n\n#### `setPreference`\n\nAlso async, this method provides you with an easy way to set a user preference. It returns `void`.\n\n**Arguments:**\n\n- `key`: the `key` of your preference to set.\n- `value`: the `value` of your preference that you're looking to set.\n\n## Example\n\nHere is an example for how you can utilize `usePreferences` within your custom Admin Panel components. Note - this example is not fully useful and is more just a reference for how to utilize the Preferences API. In this case, we are demonstrating how to set and retrieve a user's last used colors history within a `ColorPicker` or similar type component.\n\n```tsx\n'use client'\nimport React, { Fragment, useState, useEffect, useCallback } from 'react';\nimport { usePreferences } from '@payloadcms/ui'\n\nconst lastUsedColorsPreferenceKey = 'last-used-colors';\n\nconst CustomComponent = (props) =\u003e {\n const { getPreference, setPreference } = usePreferences();\n\n // Store the last used colors in local state\n const [lastUsedColors, setLastUsedColors] = useState([]);\n\n // Callback to add a color to the last used colors\n const updateLastUsedColors = useCallback((color) =\u003e {\n // First, check if color already exists in last used colors.\n // If it already exists, there is no need to update preferences\n const colorAlreadyExists = lastUsedColors.indexOf(color) \u003e -1;\n\n if (!colorAlreadyExists) {\n const newLastUsedColors = [\n ...lastUsedColors,\n color,\n ];\n\n setLastUsedColors(newLastUsedColors);\n setPreference(lastUsedColorsPreferenceKey, newLastUsedColors);\n }\n }, [lastUsedColors, setPreference]);\n\n // Retrieve preferences on component mount\n // This will only be run one time, because the `getPreference` method never changes\n useEffect(() =\u003e {\n const asyncGetPreference = async () =\u003e {\n const lastUsedColorsFromPreferences = await getPreference(lastUsedColorsPreferenceKey);\n setLastUsedColors(lastUsedColorsFromPreferences);\n };\n\n asyncGetPreference();\n }, [getPreference]);\n\n return (\n \u003cdiv\u003e\n \u003cbutton\n type=\"button\"\n onClick={() =\u003e updateLastUsedColors('red')}\n \u003e\n Use red\n \u003c/button\u003e\n \u003cbutton\n type=\"button\"\n onClick={() =\u003e updateLastUsedColors('blue')}\n \u003e\n Use blue\n \u003c/button\u003e\n \u003cbutton\n type=\"button\"\n onClick={() =\u003e updateLastUsedColors('purple')}\n \u003e\n Use purple\n \u003c/button\u003e\n \u003cbutton\n type=\"button\"\n onClick={() =\u003e updateLastUsedColors('yellow')}\n \u003e\n Use yellow\n \u003c/button\u003e\n {lastUsedColors \u0026\u0026 (\n \u003cFragment\u003e\n \u003ch5\u003eLast used colors:\u003c/h5\u003e\n \u003cul\u003e\n {lastUsedColors?.map((color) =\u003e (\n \u003cli key={color}\u003e\n {color}\n \u003c/li\u003e\n ))}\n \u003c/ul\u003e\n \u003c/Fragment\u003e\n )}\n \u003c/div\u003e\n );\n};\n\nexport default CustomComponent;\n```\n"])</script><script>self.__next_f.push([1,"77:T1080,"])</script><script>self.__next_f.push([1,"\nCustomizing the Payload [Admin Panel](./overview) through CSS alone is one of the easiest and most powerful ways to customize the look and feel of the dashboard. To allow for this level of customization, Payload:\n\n1. Exposes a [root-level stylesheet](#global-css) for you to inject custom selectors\n1. Provides a [CSS library](#css-library) that can be easily overridden or extended\n1. Uses [BEM naming conventions](http://getbem.com) so that class names are globally accessible\n\nTo customize the CSS within the Admin UI, determine scope and change you'd like to make, and then add your own CSS or SCSS to the configuration as needed.\n\n## Global CSS\n\nGlobal CSS refers to the CSS that is applied to the entire [Admin Panel](./overview). This is where you can have a significant impact to the look and feel of the Admin UI through just a few lines of code.\n\nYou can add your own global CSS through the root `custom.scss` file of your app. This file is loaded into the root of the Admin Panel and can be used to inject custom selectors or styles however needed.\n\nHere is an example of how you might target the Dashboard View and change the background color:\n\n```scss\n.dashboard {\n background-color: red; // highlight-line\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n If you are building [Custom Components](./components), it is best to import your own stylesheets directly into your components, rather than using the global stylesheet. You can continue to use the [CSS library](#css-library) as needed.\n\u003c/Banner\u003e\n\n### Specificity rules\n\nAll Payload CSS is encapsulated inside CSS layers under `@layer payload-default`. Any custom css will now have the highest possible specificity.\n\nWe have also provided a layer `@layer payload` if you want to use layers and ensure that your styles are applied after payload.\n\nTo override existing styles in a way that the previous rules of specificity would be respected you can use the default layer like so\n```css\n@layer payload-default {\n // my styles within the payload specificity\n}\n```\n\n## Re-using Payload SCSS variables and utilities\n\nYou can re-use Payload's SCSS variables and utilities in your own stylesheets by importing it from the UI package.\n\n```scss\n@import '~@payloadcms/ui/scss';\n```\n\n## CSS Library\n\nTo make it as easy as possible for you to override default styles, Payload uses [BEM naming conventions](http://getbem.com/) for all CSS within the Admin UI. If you provide your own CSS, you can override any built-in styles easily, including targeting nested components and their various component states.\n\nYou can also override Payload's built-in [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties). These variables are widely consumed by the Admin Panel, so modifying them has a significant impact on the look and feel of the Admin UI.\n\nThe following variables are defined and can be overridden:\n\n- [Breakpoints](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/queries.scss)\n- [Colors](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/colors.scss)\n - Base color shades (white to black by default)\n - Success / warning / error color shades\n - Theme-specific colors (background, input background, text color, etc.)\n - Elevation colors (used to determine how \"bright\" something should be when compared to the background)\n- [Sizing](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss/app.scss)\n - Horizontal gutter\n - Transition speeds\n - Font sizes\n - Etc.\n\nFor an up-to-date, comprehensive list of all available variables, please refer to the [Source Code](https://github.com/payloadcms/payload/blob/main/packages/ui/src/scss).\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eWarning:\u003c/strong\u003e\n If you're overriding colors or theme elevations, make sure to consider how [your changes will affect dark mode](#dark-mode).\n\u003c/Banner\u003e\n\n#### Dark Mode\n\nColors are designed to automatically adapt to theme of the [Admin Panel](./overview). By default, Payload automatically overrides all `--theme-elevation` colors and inverts all success / warning / error shades to suit dark mode. We also update some base theme variables like `--theme-bg`, `--theme-text`, etc.\n"])</script><script>self.__next_f.push([1,"78:T1083,"])</script><script>self.__next_f.push([1,"\nDocument locking in Payload ensures that only one user at a time can edit a document, preventing data conflicts and accidental overwrites. When a document is locked, other users are prevented from making changes until the lock is released, ensuring data integrity in collaborative environments.\n\nThe lock is automatically triggered when a user begins editing a document within the Admin Panel and remains in place until the user exits the editing view or the lock expires due to inactivity.\n\n## How it works\n\nWhen a user starts editing a document, Payload locks it for that user. If another user attempts to access the same document, they will be notified that it is currently being edited. They can then choose one of the following options:\n\n- View in Read-Only: View the document without the ability to make any changes.\n- Take Over: Take over editing from the current user, which locks the document for the new editor and notifies the original user.\n- Return to Dashboard: Navigate away from the locked document and continue with other tasks.\n\nThe lock will automatically expire after a set period of inactivity, configurable using the `duration` property in the `lockDocuments` configuration, after which others can resume editing.\n\n\u003cBanner type=\"info\"\u003e \u003cstrong\u003eNote:\u003c/strong\u003e If your application does not require document locking, you can disable this feature for any collection or global by setting the \u003ccode\u003elockDocuments\u003c/code\u003e property to \u003ccode\u003efalse\u003c/code\u003e. \u003c/Banner\u003e\n\n### Config Options\n\nThe `lockDocuments` property exists on both the Collection Config and the Global Config. Document locking is enabled by default, but you can customize the lock duration or turn off the feature for any collection or global.\n\nHere’s an example configuration for document locking:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Posts: CollectionConfig = {\n slug: 'posts',\n fields: [\n {\n name: 'title',\n type: 'text',\n },\n // other fields...\n ],\n lockDocuments: {\n duration: 600, // Duration in seconds\n },\n}\n```\n\n#### Locking Options\n\n| Option | Description |\n| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **`lockDocuments`** | Enables or disables document locking for the collection or global. By default, document locking is enabled. Set to an object to configure, or set to false to disable locking. |\n| **`duration`** | Specifies the duration (in seconds) for how long a document remains locked without user interaction. The default is 300 seconds (5 minutes). |\n\n### Impact on APIs\n\nDocument locking affects both the Local and REST APIs, ensuring that if a document is locked, concurrent users will not be able to perform updates or deletes on that document (including globals). If a user attempts to update or delete a locked document, they will receive an error.\n\nOnce the document is unlocked or the lock duration has expired, other users can proceed with updates or deletes as normal.\n\n#### Overriding Locks\n\nFor operations like `update` and `delete`, Payload includes an `overrideLock` option. This boolean flag, when set to `false`, enforces document locks, ensuring that the operation will not proceed if another user currently holds the lock.\n\nBy default, `overrideLock` is set to `true`, which means that document locks are ignored, and the operation will proceed even if the document is locked. To enforce locks and prevent updates or deletes on locked documents, set `overrideLock: false`.\n\n```ts\nconst result = await payload.update({\n collection: 'posts',\n id: '123',\n data: {\n title: 'New title',\n },\n overrideLock: false, // Enforces the document lock, preventing updates if the document is locked\n})\n```\n\nThis option is particularly useful in scenarios where administrative privileges or specific workflows require you to override the lock and ensure the operation is completed.\n"])</script><script>self.__next_f.push([1,"79:T2b75,"])</script><script>self.__next_f.push([1,"\n\u003cYouTube\n id=\"CT4KafeJjTI\"\n title=\"Simplified Authentication for Headless CMS: Unlocking Reusability in One Line\"\n/\u003e\n\nAuthentication is a critical part of any application. Payload provides a secure, portable way to manage user accounts out of the box. Payload Authentication is designed to be used in both the [Admin Panel](../admin/overview), all well as your own external applications, completely eliminating the need for paid, third-party platforms and services.\n\nHere are some common use cases of Authentication in your own applications:\n\n- Customer accounts for an e-commerce app\n- User accounts for a SaaS product\n- P2P apps or social sites where users need to log in and manage their profiles\n- Online games where players need to track their progress over time\n\nWhen Authentication is enabled on a [Collection](../configuration/collections), Payload injects all necessary functionality to support the entire user flow. This includes all [auth-related operations](./operations) like account creation, logging in and out, and resetting passwords, all [auth-related emails](./email) like email verification and password reset, as well as any necessary UI to manage users from the Admin Panel.\n\nTo enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections#config-options):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Users: CollectionConfig = {\n // ...\n auth: true, // highlight-line\n}\n```\n\n![Authentication Admin Panel functionality](https://payloadcms.com/images/docs/auth-admin.jpg)\n_Admin Panel screenshot depicting an Admins Collection with Auth enabled_\n\n## Config Options\n\nAny [Collection](../configuration/collections) can opt-in to supporting Authentication. Once enabled, each Document that is created within the Collection can be thought of as a \"user\". This enables a complete authentication workflow on your Collection, such as logging in and out, resetting their password, and more.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n By default, Payload provides an auth-enabled `User` Collection which is used to access the Admin Panel. [More details](../admin/overview#the-admin-user-collection).\n\u003c/Banner\u003e\n\nTo enable Authentication on a Collection, use the `auth` property in the [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Admins: CollectionConfig = {\n // ...\n // highlight-start\n auth: {\n tokenExpiration: 7200, // How many seconds to keep the user logged in\n verify: true, // Require email verification before being allowed to authenticate\n maxLoginAttempts: 5, // Automatically lock a user out after X amount of failed logins\n lockTime: 600 * 1000, // Time period to allow the max login attempts\n // More options are available\n },\n // highlight-end\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n For default auth behavior, set `auth: true`. This is a good starting point for most applications.\n\u003c/Banner\u003e\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n Auth-enabled Collections with be automatically injected with the `hash`, `salt`, and `email` fields. [More details](../fields/overview#field-names).\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Option | Description |\n|----------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`cookies`** | Set cookie options, including `secure`, `sameSite`, and `domain`. For advanced users. |\n| **`depth`** | How many levels deep a `user` document should be populated when creating the JWT and binding the `user` to the `req`. Defaults to `0` and should only be modified if absolutely necessary, as this will affect performance. |\n| **`disableLocalStrategy`** | Advanced - disable Payload's built-in local auth strategy. Only use this property if you have replaced Payload's auth mechanisms with your own. |\n| **`forgotPassword`** | Customize the way that the `forgotPassword` operation functions. [More details](./email#forgot-password). |\n| **`lockTime`** | Set the time (in milliseconds) that a user should be locked out if they fail authentication more times than `maxLoginAttempts` allows for. |\n| **`loginWithUsername`** | Ability to allow users to login with username/password. [More](../authentication/overview#login-with-username) |\n| **`maxLoginAttempts`** | Only allow a user to attempt logging in X amount of times. Automatically locks out a user from authenticating if this limit is passed. Set to `0` to disable. |\n| **`removeTokenFromResponses`** | Set to true if you want to remove the token from the returned authentication API responses such as login or refresh. |\n| **`strategies`** | Advanced - an array of custom authentication strategies to extend this collection's authentication with. [More details](./custom-strategies). |\n| **`tokenExpiration`** | How long (in seconds) to keep the user logged in. JWTs and HTTP-only cookies will both expire at the same time. |\n| **`useAPIKey`** | Payload Authentication provides for API keys to be set on each user within an Authentication-enabled Collection. [More details](./api-keys). |\n| **`verify`** | Set to `true` or pass an object with verification options to require users to verify by email before they are allowed to log into your app. [More details](./email#email-verification). |\n\n### Login With Username\n\nYou can allow users to login with their username instead of their email address by setting the `loginWithUsername` property to `true`.\n\nExample:\n\n```ts\n{\n slug: 'customers',\n auth: {\n loginWithUsername: true,\n },\n}\n```\n\nOr, you can pass an object with additional options:\n\n```ts\n{\n slug: 'customers',\n auth: {\n loginWithUsername: {\n allowEmailLogin: true, // default: false\n requireEmail: false, // default: false\n },\n },\n}\n```\n\n**`allowEmailLogin`**\n\nIf set to `true`, users can log in with either their username or email address. If set to `false`, users can only log in with their username.\n\n**`requireEmail`**\n\nIf set to `true`, an email address is required when creating a new user. If set to `false`, email is not required upon creation.\n\n## Auto-Login\n\nFor testing and demo purposes you may want to skip forcing the user to login in order to access your application. Typically, all users should be required to login, however, you can speed up local development time by enabling auto-login.\n\nTo enable auto-login, set the `autoLogin` property in the [Admin Config](../configuration/admin):\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n // ...\n // highlight-start\n autoLogin:\n process.env.NEXT_PUBLIC_ENABLE_AUTOLOGIN === 'true'\n ? {\n email: 'test@example.com',\n password: 'test',\n prefillOnly: true,\n }\n : false,\n // highlight-end\n})\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eWarning:\u003c/strong\u003e\n The recommended way to use this feature is behind an [Environment Variable](../configuration/environment-vars). This will ensure it is _disabled_ in production.\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Option | Description |\n|-------------------|-----------------------------------------------------------------------------------------------------------------|\n| **`username`** | The username of the user to login as |\n| **`email`** | The email address of the user to login as |\n| **`password`** | The password of the user to login as. This is only needed if `prefillOnly` is set to true |\n| **`prefillOnly`** | If set to true, the login credentials will be prefilled but the user will still need to click the login button. |\n\n## Operations\n\nAll auth-related operations are available via Payload's REST, Local, and GraphQL APIs. These operations are automatically added to your Collection when you enable Authentication. [More details](./operations).\n\n## Strategies\n\nOut of the box Payload ships with a three powerful Authentication strategies:\n\n- [HTTP-Only Cookies](./cookies)\n- [JSON Web Tokens (JWT)](./jwt)\n- [API-Keys](./api-keys)\n\nEach of these strategies can work together or independently. You can also create your own custom strategies to fit your specific needs. [More details](./custom-strategies).\n\n### HTTP-Only Cookies\n\n[HTTP-only cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) are a highly secure method of storing identifiable data on a user's device so that Payload can automatically recognize a returning user until their cookie expires. They are totally protected from common XSS attacks and \u003cstrong\u003ecannot be read by JavaScript in the browser\u003c/strong\u003e, unlike JWT's. [More details](./cookies).\n\n### JSON Web Tokens\n\nJWT (JSON Web Tokens) can also be utilized to perform authentication. Tokens are generated on `login`, `refresh` and `me` operations and can be attached to future requests to authenticate users. [More details](./jwt).\n\n### API Keys\n\nAPI Keys can be enabled on auth collections. These are particularly useful when you want to authenticate against Payload from a third party service. [More details](./api-keys).\n\n### Custom Strategies\n\nThere are cases where these may not be enough for your application. Payload is extendable by design so you can wire up your own strategy when you need to. [More details](./custom-strategies).\n"])</script><script>self.__next_f.push([1,"7a:T26a5,"])</script><script>self.__next_f.push([1,"\nEnabling [Authentication](./overview) on a [Collection](../configuration/collections) automatically exposes additional auth-based operations in the [Local API](../local-api/overview), [REST API](../rest-api/overview), and [GraphQL API](../graphql/overview).\n\n## Access\n\nThe Access operation returns what a logged in user can and can't do with the collections and globals that are registered via your config. This data can be immensely helpful if your app needs to show and hide certain features based on [Access Control](../access-control/overview), just as the [Admin Panel](../admin/overview) does.\n\n**REST API endpoint**:\n\n`GET http://localhost:3000/api/access`\n\nExample response:\n\n```ts\n{\n canAccessAdmin: true,\n collections: {\n pages: {\n create: {\n permission: true,\n },\n read: {\n permission: true,\n },\n update: {\n permission: true,\n },\n delete: {\n permission: true,\n },\n fields: {\n title: {\n create: {\n permission: true,\n },\n read: {\n permission: true,\n },\n update: {\n permission: true,\n },\n }\n }\n }\n }\n}\n```\n\n**Example GraphQL Query**:\n\n```graphql\nquery {\n Access {\n pages {\n read {\n permission\n }\n }\n }\n}\n```\n\nDocument access can also be queried on a collection/global basis. Access on a global can queried like `http://localhost:3000/api/global-slug/access`, Collection document access can be queried like `http://localhost:3000/api/collection-slug/access/:id`.\n\n## Me\n\nReturns either a logged in user with token or null when there is no logged in user.\n\n**REST API endpoint**:\n\n`GET http://localhost:3000/api/[collection-slug]/me`\n\nExample response:\n\n```ts\n{\n user: { // The JWT \"payload\" ;) from the logged in user\n email: 'dev@payloadcms.com',\n createdAt: \"2020-12-27T21:16:45.645Z\",\n updatedAt: \"2021-01-02T18:37:41.588Z\",\n id: \"5ae8f9bde69e394e717c8832\"\n },\n token: '34o4345324...', // The token that can be used to authenticate the user\n exp: 1609619861, // Unix timestamp representing when the user's token will expire\n}\n```\n\n**Example GraphQL Query**:\n\n```graphql\nquery {\n me[collection-singular-label] {\n user {\n email\n }\n exp\n }\n}\n```\n\n## Login\n\nAccepts an `email` and `password`. On success, it will return the logged in user as well as a token that can be used to authenticate. In the GraphQL and REST APIs, this operation also automatically sets an HTTP-only cookie including the user's token. If you pass a `res` to the Local API operation, Payload will set a cookie there as well.\n\n**Example REST API login**:\n\n```ts\nconst res = await fetch('http://localhost:3000/api/[collection-slug]/login', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n email: 'dev@payloadcms.com',\n password: 'this-is-not-our-password...or-is-it?',\n }),\n})\n\nconst json = await res.json()\n\n// JSON will be equal to the following:\n/*\n{\n user: {\n email: 'dev@payloadcms.com',\n createdAt: \"2020-12-27T21:16:45.645Z\",\n updatedAt: \"2021-01-02T18:37:41.588Z\",\n id: \"5ae8f9bde69e394e717c8832\"\n },\n token: '34o4345324...',\n exp: 1609619861\n}\n*/\n```\n\n**Example GraphQL Mutation**:\n\n```graphql\nmutation {\n login[collection-singular-label](email: \"dev@payloadcms.com\", password: \"yikes\") {\n user {\n email\n }\n exp\n token\n }\n}\n```\n\n**Example Local API login**:\n\n```ts\nconst result = await payload.login({\n collection: '[collection-slug]',\n data: {\n email: 'dev@payloadcms.com',\n password: 'get-out',\n },\n})\n```\n\n## Logout\n\nAs Payload sets HTTP-only cookies, logging out cannot be done by just removing a cookie in JavaScript, as HTTP-only cookies are inaccessible by JS within the browser. So, Payload exposes a `logout` operation to delete the token in a safe way.\n\n**Example REST API logout**:\n\n```ts\nconst res = await fetch('http://localhost:3000/api/[collection-slug]/logout', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n})\n```\n\n**Example GraphQL Mutation**:\n\n```\nmutation {\n logout[collection-singular-label]\n}\n```\n\n## Refresh\n\nAllows for \"refreshing\" JWTs. If your user has a token that is about to expire, but the user is still active and using the app, you might want to use the `refresh` operation to receive a new token by executing this operation via the authenticated user.\n\nThis operation requires a non-expired token to send back a new one. If the user's token has already expired, you will need to allow them to log in again to retrieve a new token.\n\nIf successful, this operation will automatically renew the user's HTTP-only cookie and will send back the updated token in JSON.\n\n**Example REST API token refresh**:\n\n```ts\nconst res = await fetch('http://localhost:3000/api/[collection-slug]/refresh-token', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n})\n\nconst json = await res.json()\n\n// JSON will be equal to the following:\n/*\n{\n user: {\n email: 'dev@payloadcms.com',\n createdAt: \"2020-12-27T21:16:45.645Z\",\n updatedAt: \"2021-01-02T18:37:41.588Z\",\n id: \"5ae8f9bde69e394e717c8832\"\n },\n refreshedToken: '34o4345324...',\n exp: 1609619861\n}\n*/\n```\n\n**Example GraphQL Mutation**:\n\n```\nmutation {\n refreshToken[collection-singular-label] {\n user {\n email\n }\n refreshedToken\n }\n}\n```\n\n## Verify by Email\n\nIf your collection supports email verification, the Verify operation will be exposed which accepts a verification token and sets the user's `_verified` property to `true`, thereby allowing the user to authenticate with the Payload API.\n\n**Example REST API user verification**:\n\n```ts\nconst res = await fetch(`http://localhost:3000/api/[collection-slug]/verify/${TOKEN_HERE}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n})\n```\n\n**Example GraphQL Mutation**:\n\n```graphql\nmutation {\n verifyEmail[collection-singular-label](token: \"TOKEN_HERE\")\n}\n```\n\n**Example Local API verification**:\n\n```ts\nconst result = await payload.verifyEmail({\n collection: '[collection-slug]',\n token: 'TOKEN_HERE',\n})\n```\n\n## Unlock\n\nIf a user locks themselves out and you wish to deliberately unlock them, you can utilize the Unlock operation. The [Admin Panel](../admin/overview) features an Unlock control automatically for all collections that feature max login attempts, but you can programmatically unlock users as well by using the Unlock operation.\n\nTo restrict who is allowed to unlock users, you can utilize the [`unlock`](../access-control/collections#unlock) access control function.\n\n**Example REST API unlock**:\n\n```ts\nconst res = await fetch(`http://localhost:3000/api/[collection-slug]/unlock`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n})\n```\n\n**Example GraphQL Mutation**:\n\n```\nmutation {\n unlock[collection-singular-label]\n}\n```\n\n**Example Local API unlock**:\n\n```ts\nconst result = await payload.unlock({\n collection: '[collection-slug]',\n})\n```\n\n## Forgot Password\n\nPayload comes with built-in forgot password functionality. Submitting an email address to the Forgot Password operation will generate an email and send it to the respective email address with a link to reset their password.\n\nThe link to reset the user's password contains a token which is what allows the user to securely reset their password.\n\nBy default, the Forgot Password operations send users to the [Admin Panel](../admin/overview) to reset their password, but you can customize the generated email to send users to the frontend of your app instead by [overriding the email HTML](../authentication/overview#forgot-password).\n\n**Example REST API Forgot Password**:\n\n```ts\nconst res = await fetch(`http://localhost:3000/api/[collection-slug]/forgot-password`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n email: 'dev@payloadcms.com',\n }),\n})\n```\n\n**Example GraphQL Mutation**:\n\n```\nmutation {\n forgotPassword[collection-singular-label](email: \"dev@payloadcms.com\")\n}\n```\n\n**Example Local API forgot password**:\n\n```ts\nconst token = await payload.forgotPassword({\n collection: '[collection-slug]',\n data: {\n email: 'dev@payloadcms.com',\n },\n disableEmail: false, // you can disable the auto-generation of email via local API\n})\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n \u003cbr /\u003e\n You can stop the reset-password email from being sent via using the local API. This is helpful if\n you need to create user accounts programmatically, but not set their password for them. This\n effectively generates a reset password token which you can then use to send to a page you create,\n allowing a user to \"complete\" their account by setting their password. In the background, you'd\n use the token to \"reset\" their password.\n\u003c/Banner\u003e\n\n## Reset Password\n\nAfter a user has \"forgotten\" their password and a token is generated, that token can be used to send to the reset password operation along with a new password which will allow the user to reset their password securely.\n\n**Example REST API Reset Password**:\n\n```ts\nconst res = await fetch(`http://localhost:3000/api/[collection-slug]/reset-password`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n token: 'TOKEN_GOES_HERE'\n password: 'not-today',\n }),\n});\n\nconst json = await res.json();\n\n// JSON will be equal to the following:\n/*\n{\n user: {\n email: 'dev@payloadcms.com',\n createdAt: \"2020-12-27T21:16:45.645Z\",\n updatedAt: \"2021-01-02T18:37:41.588Z\",\n id: \"5ae8f9bde69e394e717c8832\"\n },\n token: '34o4345324...',\n exp: 1609619861\n}\n*/\n```\n\n**Example GraphQL Mutation**:\n\n```graphql\nmutation {\n resetPassword[collection-singular-label](token: \"TOKEN_GOES_HERE\", password: \"not-today\")\n}\n```\n"])</script><script>self.__next_f.push([1,"7b:T207b,"])</script><script>self.__next_f.push([1,"\n[Authentication](./overview) ties directly into the [Email](../email/overview) functionality that Payload provides. This allows you to send emails to users for verification, password resets, and more. While Payload provides default email templates for these actions, you can customize them to fit your brand.\n\n## Email Verification\n\nEmail Verification forces users to prove they have access to the email address they can authenticate. This will help to reduce spam accounts and ensure that users are who they say they are.\n\nTo enable Email Verification, use the `auth.verify` property on your [Collection Config](../configuration/collections):\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Customers: CollectionConfig = {\n // ...\n auth: {\n verify: true // highlight-line\n },\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n Verification emails are fully customizable. [More details](#generateEmailHTML).\n\u003c/Banner\u003e\n\nThe following options are available:\n\n| Option | Description |\n|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users indicating how to validate their account. [More details](#generateemailhtml). |\n| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users indicating how to validate their account. [More details](#generateemailsubject). |\n\n#### generateEmailHTML\n\nFunction that accepts one argument, containing `{ req, token, user }`, that allows for overriding the HTML within emails that are sent to users indicating how to validate their account. The function should return a string that supports HTML, which can optionally be a full HTML email.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Customers: CollectionConfig = {\n // ...\n auth: {\n verify: {\n // highlight-start\n generateEmailHTML: ({ req, token, user }) =\u003e {\n // Use the token provided to allow your user to verify their account\n const url = `https://yourfrontend.com/verify?token=${token}`\n\n return `Hey ${user.email}, verify your email by clicking here: ${url}`\n },\n // highlight-end\n },\n },\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n If you specify a different URL to send your users to for email verification, such as a page on the\n frontend of your app or similar, you need to handle making the call to the Payload REST or GraphQL\n verification operation yourself on your frontend, using the token that was provided for you.\n Above, it was passed via query parameter.\n\u003c/Banner\u003e\n\n#### generateEmailSubject\n\nSimilarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Customers: CollectionConfig = {\n // ...\n auth: {\n verify: {\n // highlight-start\n generateEmailSubject: ({ req, user }) =\u003e {\n return `Hey ${user.email}, reset your password!`;\n }\n // highlight-end\n }\n }\n}\n```\n\n## Forgot Password\n\nYou can customize how the Forgot Password workflow operates with the following options on the `auth.forgotPassword` property:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Customers: CollectionConfig = {\n // ...\n auth: {\n forgotPassword: { // highlight-line\n // ...\n },\n },\n}\n```\n\nThe following options are available:\n\n| Option | Description |\n|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`generateEmailHTML`** | Allows for overriding the HTML within emails that are sent to users attempting to reset their password. [More details](#generateEmailHTML). |\n| **`generateEmailSubject`** | Allows for overriding the subject of the email that is sent to users attempting to reset their password. [More details](#generateEmailSubject). |\n\n#### generateEmailHTML\n\nThis function allows for overriding the HTML within emails that are sent to users attempting to reset their password. The function should return a string that supports HTML, which can be a full HTML email.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Customers: CollectionConfig = {\n // ...\n auth: {\n forgotPassword: {\n // highlight-start\n generateEmailHTML: ({ req, token, user }) =\u003e {\n // Use the token provided to allow your user to reset their password\n const resetPasswordURL = `https://yourfrontend.com/reset-password?token=${token}`\n\n return `\n \u003c!doctype html\u003e\n \u003chtml\u003e\n \u003cbody\u003e\n \u003ch1\u003eHere is my custom email template!\u003c/h1\u003e\n \u003cp\u003eHello, ${user.email}!\u003c/p\u003e\n \u003cp\u003eClick below to reset your password.\u003c/p\u003e\n \u003cp\u003e\n \u003ca href=\"${resetPasswordURL}\"\u003e${resetPasswordURL}\u003c/a\u003e\n \u003c/p\u003e\n \u003c/body\u003e\n \u003c/html\u003e\n `\n },\n // highlight-end\n },\n },\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n If you specify a different URL to send your users to for resetting their password, such as a page\n on the frontend of your app or similar, you need to handle making the call to the Payload REST or\n GraphQL reset-password operation yourself on your frontend, using the token that was provided for\n you. Above, it was passed via query parameter.\n\u003c/Banner\u003e\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n HTML templating can be used to create custom email templates, inline CSS automatically, and more.\n You can make a reusable function that standardizes all email sent from Payload, which makes\n sending custom emails more DRY. Payload doesn't ship with an HTML templating engine, so you are\n free to choose your own.\n\u003c/Banner\u003e\n\nThe following arguments are passed to the `generateEmailHTML` function:\n\n| Argument | Description |\n|----------|-----------------------------------------------------------------------------------------------|\n| `req` | The request object. |\n| `token` | The token that is generated for the user to reset their password. |\n| `user` | The user document that is attempting to reset their password. |\n\n#### generateEmailSubject\n\nSimilarly to the above `generateEmailHTML`, you can also customize the subject of the email. The function argument are the same but you can only return a string - not HTML.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Customers: CollectionConfig = {\n // ...\n auth: {\n forgotPassword: {\n // highlight-start\n generateEmailSubject: ({ req, user }) =\u003e {\n return `Hey ${user.email}, reset your password!`;\n }\n // highlight-end\n }\n }\n}\n```\n\nThe following arguments are passed to the `generateEmailSubject` function:\n\n| Argument | Description |\n|----------|-----------------------------------------------------------------------------------------------|\n| `req` | The request object. |\n| `user` | The user document that is attempting to reset their password. |\n"])</script><script>self.__next_f.push([1,"7c:T1382,"])</script><script>self.__next_f.push([1,"\nPayload offers the ability to [Authenticate](./overview) via HTTP-only cookies. These can be read from the responses of `login`, `logout`, `refresh`, and `me` auth operations.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n You can access the logged-in user from within [Access Control](../access-control/overview) and [Hooks](../hooks/overview) through the `req.user` argument. [More details](./token-data).\n\u003c/Banner\u003e\n\n### Automatic browser inclusion\n\nModern browsers automatically include `http-only` cookies when making requests directly to URLs—meaning that if you are running your API on `https://example.com`, and you have logged in and visit `https://example.com/test-page`, your browser will automatically include the Payload authentication cookie for you.\n\n### HTTP Authentication\n\nHowever, if you use `fetch` or similar APIs to retrieve Payload resources from its REST or GraphQL API, you must specify to include credentials (cookies).\n\nFetch example, including credentials:\n\n```ts\nconst response = await fetch('http://localhost:3000/api/pages', {\n credentials: 'include',\n})\n\nconst pages = await response.json()\n```\n\nFor more about including cookies in requests from your app to your Payload API, [read the MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Sending_a_request_with_credentials_included).\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n To make sure you have a Payload cookie set properly in your browser after logging in, you can use\n the browsers Developer Tools \u003e Application \u003e Cookies \u003e [your-domain-here]. The Developer tools\n will still show HTTP-only cookies.\n\u003c/Banner\u003e\n\n### CSRF Attacks\n\nCSRF (cross-site request forgery) attacks are common and dangerous. By using an HTTP-only cookie, Payload removes many XSS vulnerabilities, however, CSRF attacks can still be possible.\n\nFor example, let's say you have a popular app `https://payload-finances.com` that allows users to manage finances, send and receive money. As Payload is using HTTP-only cookies, that means that browsers automatically will include cookies when sending requests to your domain - \u003cstrong\u003eno matter what page created the request\u003c/strong\u003e.\n\nSo, if a user of `https://payload-finances.com` is logged in and is browsing around on the internet, they might stumble onto a page with malicious intent. Let's look at an example:\n\n```ts\n// malicious-intent.com\n// makes an authenticated request as on your behalf\n\nconst maliciousRequest = await fetch(`https://payload-finances.com/api/me`, {\n credentials: 'include'\n}).then(res =\u003e await res.json())\n```\n\nIn this scenario, if your cookie was still valid, malicious-intent.com would be able to make requests like the one above on your behalf. This is a CSRF attack.\n\n### CSRF Prevention\n\nDefine domains that your trust and are willing to accept Payload HTTP-only cookie based requests from. Use the `csrf` option on the base Payload Config to do this:\n\n```ts\n// payload.config.ts\n\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n serverURL: 'https://my-payload-instance.com',\n // highlight-start\n csrf: [\n // whitelist of domains to allow cookie auth from\n 'https://your-frontend-app.com',\n 'https://your-other-frontend-app.com',\n // `config.serverURL` is added by default if defined\n ],\n // highlight-end\n collections: [\n // collections here\n ],\n})\n\nexport default config\n```\n\n#### Cross domain authentication\n\nIf your frontend is on a different domain than your Payload API then you will not be able to use HTTP-only cookies for authentication by default as they will be considered third-party cookies by the browser.\nThere are a few strategies to get around this:\n\n##### 1. Use subdomains\n\nCookies can cross subdomains without being considered third party cookies, for example if your API is at api.example.com then you can authenticate from example.com.\n\n##### 2. Configure cookies\n\nIf option 1 isn't possible, then you can get around this limitation by [configuring your cookies](./overview#config-options) on your authentication collection to achieve the following setup:\n\n```\nSameSite: None // allows the cookie to cross domains\nSecure: true // ensures its sent over HTTPS only\nHttpOnly: true // ensures its not accessible via client side JavaScript\n```\n\nConfiguration example:\n\n```ts\n{\n slug: 'users',\n auth: {\n cookies: {\n sameSite: 'None',\n secure: true,\n }\n },\n fields: [\n // your auth fields here\n ]\n},\n```\n\nIf you're configuring [cors](../production/preventing-abuse#cross-origin-resource-sharing-cors) in your Payload config, you won't be able to use a wildcard anymore, you'll need to specify the list of allowed domains.\n\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eGood to know:\u003c/strong\u003e\n Setting up \u003ccode\u003esecure: true\u003c/code\u003e will not work if you're developing on \u003ccode\u003ehttp://localhost\u003c/code\u003e or any non-https domain. For local development you should conditionally set this to \u003ccode\u003efalse\u003c/code\u003e based on the environment.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"7d:T553,\nPayload offers the ability to [Authenticate](./overview) via JSON Web Tokens (JWT). These can be read from the responses of `login`, `logout`, `refresh`, and `me` auth operations.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n You can access the logged-in user from within [Access Control](../access-control/overview) and [Hooks](../hooks/overview) through the `req.user` argument. [More details](./token-data).\n\u003c/Banner\u003e\n\n### Identifying Users Via The Authorization Header\n\nIn addition to authenticating via an HTTP-only cookie, you can also identify users via the `Authorization` header on an HTTP request.\n\nExample:\n\n```ts\nconst user = await fetch('http://localhost:3000/api/users/login', {\n method: 'POST',\n body: JSON.stringify({\n email: 'dev@payloadcms.com',\n password: 'password',\n })\n}).then(req =\u003e await req.json())\n\nconst request = await fetch('http://localhost:3000', {\n headers: {\n Authorization: `JWT ${user.token}`,\n },\n})\n```\n\n### Omitting The Token\n\nIn some cases you may want to prevent the token from being returned from the auth operations. You can do that by setting `removeTokenFromResponse` to `true` like so:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const UsersWithoutJWTs: CollectionConfig = {\n slug: 'users-without-jwts',\n auth: {\n removeTokenFromRepsonse: true, // highlight-line\n },\n}\n```\n7e:Tef2,"])</script><script>self.__next_f.push([1,"\nTo integrate with third-party APIs or services, you might need the ability to generate API keys that can be used to identify as a certain user within Payload. API keys are generated on a user-by-user basis, similar to email and passwords, and are meant to represent a single user.\n\nFor example, if you have a third-party service or external app that needs to be able to perform protected actions against Payload, first you need to create a user within Payload, i.e. `dev@thirdparty.com`. From your external application you will need to authenticate with that user, you have two options:\n\n1. Log in each time with that user and receive an expiring token to request with.\n1. Generate a non-expiring API key for that user to request with.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n \u003cbr/\u003e\n This is particularly useful as you can create a \"user\" that reflects an integration with a specific external service and assign a \"role\" or specific access only needed by that service/integration.\n\u003c/Banner\u003e\n\nTechnically, both of these options will work for third-party integrations but the second option with API key is simpler, because it reduces the amount of work that your integrations need to do to be authenticated properly.\n\nTo enable API keys on a collection, set the `useAPIKey` auth option to `true`. From there, a new interface will appear in the [Admin Panel](../admin/overview) for each document within the collection that allows you to generate an API key for each user in the Collection.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ThirdPartyAccess: CollectionConfig = {\n slug: 'third-party-access',\n auth: {\n useAPIKey: true, // highlight-line\n },\n fields: [],\n}\n```\n\nUser API keys are encrypted within the database, meaning that if your database is compromised,\nyour API keys will not be.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n If you change your `PAYLOAD_SECRET`, you will need to regenerate your API keys.\n \u003cbr /\u003e\n The secret key is used to encrypt the API keys, so if you change the secret, existing API keys will\n no longer be valid.\n\u003c/Banner\u003e\n\n### HTTP Authentication\n\nTo authenticate REST or GraphQL API requests using an API key, set the `Authorization` header. The header is case-sensitive and needs the slug of the `auth.useAPIKey` enabled collection, then \" API-Key \", followed by the `apiKey` that has been assigned. Payload's built-in middleware will then assign the user document to `req.user` and handle requests with the proper [Access Control](../access-control/overview). By doing this, Payload recognizes the request being made as a request by the user associated with that API key.\n\n**For example, using Fetch:**\n\n```ts\nimport Users from '../collections/Users'\n\nconst response = await fetch('http://localhost:3000/api/pages', {\n headers: {\n Authorization: `${Users.slug} API-Key ${YOUR_API_KEY}`,\n },\n})\n```\n\nPayload ensures that the same, uniform [Access Control](../access-control/overview) is used across all authentication strategies. This enables you to utilize your existing Access Control configurations with both API keys and the standard email/password authentication. This consistency can aid in maintaining granular control over your API keys.\n\n### API Key Only Auth\n\nIf you want to use API keys as the only authentication method for a collection, you can disable the default local strategy by setting `disableLocalStrategy` to `true` on the collection's `auth` property. This will disable the ability to authenticate with email and password, and will only allow for authentication via API key.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const ThirdPartyAccess: CollectionConfig = {\n slug: 'third-party-access',\n auth: {\n useAPIKey: true,\n disableLocalStrategy: true, // highlight-line\n },\n}\n```\n"])</script><script>self.__next_f.push([1,"7f:Tc8f,"])</script><script>self.__next_f.push([1,"\n\u003cBanner type=\"warning\"\u003e\n This is an advanced feature, so only attempt this if you are an experienced developer. Otherwise,\n just let Payload's built-in authentication handle user auth for you.\n\u003c/Banner\u003e\n\n### Creating a strategy\n\nAt the core, a strategy is a way to authenticate a user making a request. As of `3.0` we moved away from [Passport](https://www.passportjs.org) in favor of pulling back the curtain and putting you in full control.\n\nA strategy is made up of the following:\n\n| Parameter | Description |\n| --------------------------- | ------------------------------------------------------------------------- |\n| **`name`** \\* | The name of your strategy |\n| **`authenticate`**\u0026nbsp;\\* | A function that takes in the parameters below and returns a user or null. |\n\nThe `authenticate` function is passed the following arguments:\n\n| Argument | Description |\n| ------------------- | ------------------------------------------------------------------------------------------------- |\n| **`headers`** \\* | The headers on the incoming request. Useful for retrieving identifiable information on a request. |\n| **`payload`** \\* | The Payload class. Useful for authenticating the identifiable information against Payload. |\n| **`isGraphQL`** | Whether or not the request was made from a GraphQL endpoint. Default is `false`. |\n\n\n### Example Strategy\n\nAt its core a strategy simply takes information from the incoming request and returns a user. This is exactly how Payload's built-in strategies function.\n\nYour `authenticate` method should return an object containing a Payload user document and any optional headers that you'd like Payload to set for you when we return a response.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Users: CollectionConfig = {\n slug: 'users',\n auth: {\n disableLocalStrategy: true,\n // highlight-start\n strategies: [\n {\n name: 'custom-strategy',\n authenticate: ({ payload, headers }) =\u003e {\n const usersQuery = await payload.find({\n collection: 'users',\n where: {\n code: {\n equals: headers.get('code'),\n },\n secret: {\n equals: headers.get('secret'),\n },\n },\n })\n\n return {\n // Send the user back to authenticate,\n // or send null if no user should be authenticated\n user: usersQuery.docs[0] || null,\n\n // Optionally, you can return headers\n // that you'd like Payload to set here when\n // it returns the response\n responseHeaders: new Headers({\n 'some-header': 'my header value'\n })\n }\n }\n }\n ]\n // highlight-end\n },\n fields: [\n {\n name: 'code',\n type: 'text',\n index: true,\n unique: true,\n },\n {\n name: 'secret',\n type: 'text',\n },\n ]\n}\n```\n"])</script><script>self.__next_f.push([1,"80:T8ea,"])</script><script>self.__next_f.push([1,"\nDuring the lifecycle of a request you will be able to access the data you have configured to be stored in the JWT by accessing `req.user`. The user object is automatically appended to the request for you.\n\n### Definining Token Data\n\nYou can specify what data gets encoded to the Cookie/JWT-Token by setting `saveToJWT` property on fields within your auth collection.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Users: CollectionConfig = {\n slug: 'users',\n auth: true,\n fields: [\n {\n // will be stored in the JWT\n saveToJWT: true,\n type: 'select',\n name: 'role',\n options: [\n 'super-admin',\n 'user',\n ]\n },\n {\n // the entire object will be stored in the JWT\n // tab fields can do the same thing!\n saveToJWT: true,\n type: 'group',\n name: 'group1',\n fields: [\n {\n type: 'text',\n name: 'includeField',\n },\n {\n // will be omitted from the JWT\n saveToJWT: false,\n type: 'text',\n name: 'omitField',\n },\n ]\n },\n {\n type: 'group',\n name: 'group2',\n fields: [\n {\n // will be stored in the JWT\n // but stored at the top level\n saveToJWT: true,\n type: 'text',\n name: 'includeField',\n },\n {\n type: 'text',\n name: 'omitField',\n },\n ]\n },\n ]\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n \u003cbr/\u003e\n If you wish to use a different key other than the field `name`, you can define `saveToJWT` as a string.\n\u003c/Banner\u003e\n\n\n### Using Token Data\n\nThis is especially helpful when writing [Hooks](../hooks/overview) and [Access Control](../access-control/overview) that depend on user defined fields.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Invoices: CollectionConfig = {\n slug: 'invoices',\n access: {\n read: ({ req, data }) =\u003e {\n if (!req?.user) return false\n // highlight-start\n if ({ req.user?.role === 'super-admin'}) {\n return true\n }\n // highlight-end\n return data.owner === req.user.id\n }\n }\n fields: [\n {\n name: 'owner',\n relationTo: 'users'\n },\n // ... other fields\n ],\n}\n```\n"])</script><script>self.__next_f.push([1,"81:T401,\nPayload currently supports two official rich text editors and you can choose either one depending on your needs.\n\n1. [SlateJS](../rich-text/slate) - stable, backwards-compatible with 1.0\n2. [Lexical](../lexical/overview) - recommended\n\nThese editors are built on an \"adapter pattern\" which means that you will need to install the editor you'd like to use. Take a look at the docs for the editor you'd like to use for instructions on how to install it.\n\nThe big TL;DR here is that Slate is what we have used in the past, and we still support it for existing projects, but if you're building something new and you're feeling adventurous, you should give Lexical a shot. Slate has a lot of good stuff, but Lexical has lots more.\n\nNo matter which editor you use, you have to install it at the top-level on the `config.editor` property, which will then cascade throughout all of your rich text fields and be used accordingly. Additionally, you also have the option to override the editor on a field-by-field basis if you'd like.\n82:T313e,"])</script><script>self.__next_f.push([1,"\nThe Slate editor has been supported by Payload since we released our initial beta. It's battle-tested and will continue to be supported into the future.\n\nIf you are migrating a Payload project from 1.x or earlier, you can continue to use the Slate editor as long as you'd like.\n\nTo use the Slate editor, first you need to install it:\n\n```\nnpm install --save @payloadcms/richtext-slate\n```\n\nAfter installation, you can pass it to your top-level Payload Config:\n\n```ts\nimport { buildConfig } from 'payload'\nimport { slateEditor } from '@payloadcms/richtext-slate'\n\nexport default buildConfig({\n collections: [\n // your collections here\n ],\n // Pass the Slate editor to the root config\n editor: slateEditor({}),\n})\n```\n\nAnd here's an example for how to install the Slate editor on a field-by-field basis, while customizing its options:\n\n```ts\nimport type { CollectionConfig } from 'payload'\nimport { slateEditor } from '@payloadcms/richtext-slate'\n\nexport const Pages: CollectionConfig = {\n slug: 'pages',\n fields: [\n {\n name: 'content',\n type: 'richText',\n // Pass the Slate editor here and configure it accordingly\n editor: slateEditor({\n admin: {\n elements: [\n // customize elements allowed in Slate editor here\n ],\n leaves: [\n // customize leaves allowed in Slate editor here\n ],\n },\n }),\n },\n ],\n}\n```\n\n## Admin Options\n\n**`elements`**\n\nThe `elements` property is used to specify which built-in or custom [SlateJS elements](https://docs.slatejs.org/concepts/02-nodes#element) should be made available to the field within the Admin Panel.\n\nThe default `elements` available in Payload are:\n\n- `h1`\n- `h2`\n- `h3`\n- `h4`\n- `h5`\n- `h6`\n- `blockquote`\n- `link`\n- `ol`\n- `ul`\n- `textAlign`\n- `indent`\n- [`relationship`](#relationship-element)\n- [`upload`](#upload-element)\n- [`textAlign`](#text-align)\n\n**`leaves`**\n\nThe `leaves` property specifies built-in or custom [SlateJS leaves](https://docs.slatejs.org/concepts/08-rendering#leaves) to be enabled within the Admin Panel.\n\nThe default `leaves` available in Payload are:\n\n- `bold`\n- `code`\n- `italic`\n- `strikethrough`\n- `underline`\n\n**`link.fields`**\n\nThis allows [fields](../fields/overview) to be saved as extra fields on a link inside the Rich Text Editor. When this is present, the fields will render inside a modal that can be opened by clicking the \"edit\" button on the link element.\n\n`link.fields` may either be an array of fields (in which case all fields defined in it will be appended below the default fields) or a function that accepts the default fields as only argument and returns an array defining the entirety of fields to be used (thus providing a mechanism of overriding the default fields).\n\n![RichText link fields](https://payloadcms.com/images/docs/fields/richText/rte-link-fields-modal.jpg)\n_RichText link with custom fields_\n\n**`upload.collections[collection-name].fields`**\n\nThis allows [fields](../fields/overview) to be saved as meta data on an upload field inside the Rich Text Editor. When this is present, the fields will render inside a modal that can be opened by clicking the \"edit\" button on the upload element.\n\n![RichText upload element](https://payloadcms.com/images/docs/fields/richText/rte-upload-element.jpg)\n_RichText field using the upload element_\n\n![RichText upload element modal](https://payloadcms.com/images/docs/fields/richText/rte-upload-fields-modal.jpg)\n_RichText upload element modal displaying fields from the config_\n\n### Relationship element\n\nThe built-in `relationship` element is a powerful way to reference other Documents directly within your Rich Text editor.\n\n### Upload element\n\nSimilar to the `relationship` element, the `upload` element is a user-friendly way to reference [Upload-enabled collections](../upload/overview) with a UI specifically designed for media / image-based uploads.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n \u003cbr /\u003e\n Collections are automatically allowed to be selected within the Rich Text relationship and upload\n elements by default. If you want to disable a collection from being able to be referenced in Rich\n Text fields, set the collection admin options of \u003cstrong\u003eenableRichTextLink\u003c/strong\u003e and{' '}\n \u003cstrong\u003eenableRichTextRelationship\u003c/strong\u003e to false.\n\u003c/Banner\u003e\n\nRelationship and Upload elements are populated dynamically into your Rich Text field' content. Within the REST and Local APIs, any present RichText `relationship` or `upload` elements will respect the `depth` option that you pass, and will be populated accordingly. In GraphQL, each `richText` field accepts an argument of `depth` for you to utilize.\n\n### TextAlign element\n\nText Alignment is not included by default and can be added to a Rich Text Editor by adding `textAlign` to the list of elements. TextAlign will alter the existing element to include a new `textAlign` field in the resulting JSON. This field can be used in combination with other elements and leaves to position content to the left, center or right.\n\n### Specifying which elements and leaves to allow\n\nTo specify which default elements or leaves should be allowed to be used for this field, define arrays that contain string names for each element or leaf you wish to enable. To specify a custom element or leaf, pass an object with all corresponding properties as outlined below. View the [example](#example) to reference how this all works.\n\n### Building custom elements and leaves\n\nYou can design and build your own Slate elements and leaves to extend the editor with your own functionality. To do so, first start by reading the [SlateJS documentation](https://docs.slatejs.org/) and looking at the [Slate examples](https://www.slatejs.org/examples/richtext) to familiarize yourself with the SlateJS editor as a whole.\n\nOnce you're up to speed with the general concepts involved, you can pass in your own elements and leaves to your field's admin config.\n\n**Both custom elements and leaves are defined via the following config:**\n\n| Property | Description |\n| --------------- | ---------------------------------------------------------- |\n| **`name`** \\* | The default name to be used as a `type` for this element. |\n| **`Button`** \\* | A React component to be rendered in the Rich Text toolbar. |\n| **`plugins`** | An array of plugins to provide to the Rich Text editor. |\n| **`type`** | A type that overrides the default type used by `name` |\n\nCustom `Element`s also require the `Element` property set to a React component to be rendered as the `Element` within the rich text editor itself.\n\nCustom `Leaf` objects follow a similar pattern but require you to define the `Leaf` property instead.\n\nSpecifying custom `Type`s let you extend your custom elements by adding additional fields to your JSON object.\n\n### Example\n\n`collections/ExampleCollection.ts`\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nimport { slateEditor } from '@payloadcms/richtext-slate'\n\nexport const ExampleCollection: CollectionConfig = {\n slug: 'example-collection',\n fields: [\n {\n name: 'content', // required\n type: 'richText', // required\n defaultValue: [\n {\n children: [{ text: 'Here is some default content for this field' }],\n },\n ],\n required: true,\n editor: slateEditor({\n admin: {\n elements: [\n 'h2',\n 'h3',\n 'h4',\n 'link',\n 'blockquote',\n {\n name: 'cta',\n Button: CustomCallToActionButton,\n Element: CustomCallToActionElement,\n plugins: [\n // any plugins that are required by this element go here\n ],\n },\n ],\n leaves: [\n 'bold',\n 'italic',\n {\n name: 'highlight',\n Button: CustomHighlightButton,\n Leaf: CustomHighlightLeaf,\n plugins: [\n // any plugins that are required by this leaf go here\n ],\n },\n ],\n link: {\n // Inject your own fields into the Link element\n fields: [\n {\n name: 'rel',\n label: 'Rel Attribute',\n type: 'select',\n hasMany: true,\n options: ['noopener', 'noreferrer', 'nofollow'],\n },\n ],\n },\n upload: {\n collections: {\n media: {\n fields: [\n // any fields that you would like to save\n // on an upload element in the `media` collection\n ],\n },\n },\n },\n },\n }),\n },\n ],\n}\n```\n\n### Generating HTML\n\nAs the Rich Text field saves its content in a JSON format, you'll need to render it as HTML yourself. Here is an example for how to generate JSX / HTML from Rich Text content:\n\n```ts\nimport React, { Fragment } from \"react\";\nimport escapeHTML from \"escape-html\";\nimport { Text } from \"slate\";\n\nconst serialize = (children) =\u003e\n children.map((node, i) =\u003e {\n if (Text.isText(node)) {\n let text = (\n \u003cspan dangerouslySetInnerHTML={{ __html: escapeHTML(node.text) }} /\u003e\n );\n\n if (node.bold) {\n text = \u003cstrong key={i}\u003e{text}\u003c/strong\u003e;\n }\n\n if (node.code) {\n text = \u003ccode key={i}\u003e{text}\u003c/code\u003e;\n }\n\n if (node.italic) {\n text = \u003cem key={i}\u003e{text}\u003c/em\u003e;\n }\n\n // Handle other leaf types here...\n\n return \u003cFragment key={i}\u003e{text}\u003c/Fragment\u003e;\n }\n\n if (!node) {\n return null;\n }\n\n switch (node.type) {\n case \"h1\":\n return \u003ch1 key={i}\u003e{serialize(node.children)}\u003c/h1\u003e;\n // Iterate through all headings here...\n case \"h6\":\n return \u003ch6 key={i}\u003e{serialize(node.children)}\u003c/h6\u003e;\n case \"blockquote\":\n return \u003cblockquote key={i}\u003e{serialize(node.children)}\u003c/blockquote\u003e;\n case \"ul\":\n return \u003cul key={i}\u003e{serialize(node.children)}\u003c/ul\u003e;\n case \"ol\":\n return \u003col key={i}\u003e{serialize(node.children)}\u003c/ol\u003e;\n case \"li\":\n return \u003cli key={i}\u003e{serialize(node.children)}\u003c/li\u003e;\n case \"link\":\n return (\n \u003ca href={escapeHTML(node.url)} key={i}\u003e\n {serialize(node.children)}\n \u003c/a\u003e\n );\n\n default:\n return \u003cp key={i}\u003e{serialize(node.children)}\u003c/p\u003e;\n }\n });\n```\n\n\u003cBanner\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n The above example is for how to render to JSX, although for plain HTML the pattern is similar.\n Just remove the JSX and return HTML strings instead!\n\u003c/Banner\u003e\n\n### Built-in SlateJS Plugins\n\nPayload comes with a few built-in SlateJS plugins which can be extended to make developing your own elements and leaves a bit easier.\n\n#### `shouldBreakOutOnEnter`\n\nPayload's built-in heading elements all allow a \"hard return\" to \"break out\" of the currently active element. For example, if you hit `enter` while typing an `h1`, the `h1` will be \"broken out of\" and you'll be able to continue writing as the default paragraph element.\n\nIf you want to utilize this functionality within your own custom elements, you can do so by adding a custom plugin to your `element` like the following \"large body\" element example:\n\n`customLargeBodyElement.js`:\n\n```ts\nimport Button from './Button'\nimport Element from './Element'\nimport withLargeBody from './plugin'\n\nexport default {\n name: 'large-body',\n Button,\n Element,\n plugins: [\n (incomingEditor) =\u003e {\n const editor = incomingEditor\n const { shouldBreakOutOnEnter } = editor\n\n editor.shouldBreakOutOnEnter = (element) =\u003e\n element.type === 'large-body' ? true : shouldBreakOutOnEnter(element)\n\n return editor\n },\n ],\n}\n```\n\nAbove, you can see that we are creating a custom SlateJS element with a name of `large-body`. This might render a slightly larger body copy on the frontend of your app(s). We pass it a name, button, and element\u0026mdash;but additionally, we pass it a `plugins` array containing a single SlateJS plugin.\n\nThe plugin itself extends Payload's built-in `shouldBreakOutOnEnter` Slate function to add its own element name to the list of elements that should \"break out\" when the `enter` key is pressed.\n\n### TypeScript\n\nIf you are building your own custom Rich Text elements or leaves, you may benefit from importing the following types:\n\n```ts\nimport type { RichTextCustomElement, RichTextCustomLeaf } from '@payloadcms/richtext-slate'\n```\n"])</script><script>self.__next_f.push([1,"83:T3e30,"])</script><script>self.__next_f.push([1,"\nOne of Payload's goals is to build the best rich text editor experience that we possibly can. We want to combine the beauty and polish of the Medium editing experience with the strength and features of the Notion editor - all in one place.\n\nClassically, we've used SlateJS to work toward this goal, but building custom elements into Slate has proven to be more difficult than we'd like, and we've been keeping our options open.\n\nLexical is extremely impressive and trivializes a lot of the hard parts of building new elements into a rich text editor. It has a few distinct advantages over Slate, including the following:\n\n1. A \"/\" menu, which allows editors to easily add new elements while never leaving their keyboard\n1. A \"hover\" toolbar that pops up if you select text\n1. It supports Payload blocks natively, directly within your rich text editor\n1. Custom elements, called \"features\", are much easier to build in Lexical vs. Slate\n\nTo use the Lexical editor, first you need to install it:\n\n```\nnpm install @payloadcms/richtext-lexical\n```\n\nOnce you have it installed, you can pass it to your top-level Payload Config as follows:\n\n```ts\nimport { buildConfig } from 'payload'\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\n\nexport default buildConfig({\n collections: [\n // your collections here\n ],\n // Pass the Lexical editor to the root config\n editor: lexicalEditor({}),\n})\n```\n\nYou can also override Lexical settings on a field-by-field basis as follows:\n\n```ts\nimport type { CollectionConfig } from 'payload'\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\n\nexport const Pages: CollectionConfig = {\n slug: 'pages',\n fields: [\n {\n name: 'content',\n type: 'richText',\n // Pass the Lexical editor here and override base settings as necessary\n editor: lexicalEditor({}),\n },\n ],\n}\n```\n\n## Extending the lexical editor with Features\n\nLexical has been designed with extensibility in mind. Whether you're aiming to introduce new functionalities or tweak the existing ones, Lexical makes it seamless for you to bring those changes to life.\n\n### Features: The Building Blocks\n\nAt the heart of Lexical's customization potential are \"features\". While Lexical ships with a set of default features we believe are essential for most use cases, the true power lies in your ability to redefine, expand, or prune these as needed.\n\nIf you remove all the default features, you're left with a blank editor. You can then add in only the features you need, or you can build your own custom features from scratch.\n\n### Integrating New Features\n\nTo weave in your custom features, utilize the `features` prop when initializing the Lexical Editor. Here's a basic example of how this is done:\n\n```ts\nimport {\n BlocksFeature,\n LinkFeature,\n UploadFeature,\n lexicalEditor,\n} from '@payloadcms/richtext-lexical'\nimport { Banner } from '../blocks/Banner'\nimport { CallToAction } from '../blocks/CallToAction'\n\n{\n editor: lexicalEditor({\n features: ({ defaultFeatures, rootFeatures }) =\u003e [\n ...defaultFeatures,\n LinkFeature({\n // Example showing how to customize the built-in fields\n // of the Link feature\n fields: ({ defaultFields }) =\u003e [\n ...defaultFields,\n {\n name: 'rel',\n label: 'Rel Attribute',\n type: 'select',\n hasMany: true,\n options: ['noopener', 'noreferrer', 'nofollow'],\n admin: {\n description:\n 'The rel attribute defines the relationship between a linked resource and the current document. This is a custom link field.',\n },\n },\n ],\n }),\n UploadFeature({\n collections: {\n uploads: {\n // Example showing how to customize the built-in fields\n // of the Upload feature\n fields: [\n {\n name: 'caption',\n type: 'richText',\n editor: lexicalEditor(),\n },\n ],\n },\n },\n }),\n // This is incredibly powerful. You can re-use your Payload blocks\n // directly in the Lexical editor as follows:\n BlocksFeature({\n blocks: [Banner, CallToAction],\n }),\n ],\n })\n}\n```\n\n`features` can be both an array of features, or a function returning an array of features. The function provides the following props:\n\n\n| Prop | Description |\n|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`defaultFeatures`** | This opinionated array contains all \"recommended\" default features. You can see which features are included in the default features in the table below. |\n| **`rootFeatures`** | This array contains all features that are enabled in the root richText editor (the one defined in the payload.config.ts). If this field is the root richText editor, or if the root richText editor is not a lexical editor, this array will be empty. |\n\n\n## Features overview\n\nHere's an overview of all the included features:\n\n| Feature Name | Included by default | Description |\n|---------------------------------|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`BoldTextFeature`** | Yes | Handles the bold text format |\n| **`ItalicTextFeature`** | Yes | Handles the italic text format |\n| **`UnderlineTextFeature`** | Yes | Handles the underline text format |\n| **`StrikethroughTextFeature`** | Yes | Handles the strikethrough text format |\n| **`SubscriptTextFeature`** | Yes | Handles the subscript text format |\n| **`SuperscriptTextFeature`** | Yes | Handles the superscript text format |\n| **`InlineCodeTextFeature`** | Yes | Handles the inline-code text format |\n| **`ParagraphFeature`** | Yes | Handles paragraphs. Since they are already a key feature of lexical itself, this Feature mainly handles the Slash and Add-Block menu entries for paragraphs |\n| **`HeadingFeature`** | Yes | Adds Heading Nodes (by default, H1 - H6, but that can be customized) |\n| **`AlignFeature`** | Yes | Allows you to align text left, centered and right |\n| **`IndentFeature`** | Yes | Allows you to indent text with the tab key |\n| **`UnorderedListFeature`** | Yes | Adds unordered lists (ul) |\n| **`OrderedListFeature`** | Yes | Adds ordered lists (ol) |\n| **`CheckListFeature`** | Yes | Adds checklists |\n| **`LinkFeature`** | Yes | Allows you to create internal and external links |\n| **`RelationshipFeature`** | Yes | Allows you to create block-level (not inline) relationships to other documents |\n| **`BlockQuoteFeature`** | Yes | Allows you to create block-level quotes |\n| **`UploadFeature`** | Yes | Allows you to create block-level upload nodes - this supports all kinds of uploads, not just images |\n| **`HorizontalRuleFeature`** | Yes | Horizontal rules / separators. Basically displays an `\u003chr\u003e` element |\n| **`InlineToolbarFeature`** | Yes | The inline toolbar is the floating toolbar which appears when you select text. This toolbar only contains actions relevant for selected text |\n| **`FixedToolbarFeature`** | No | This classic toolbar is pinned to the top and always visible. Both inline and fixed toolbars can be enabled at the same time. |\n| **`BlocksFeature`** | No | Allows you to use Payload's [Blocks Field](../fields/blocks) directly inside your editor. In the feature props, you can specify the allowed blocks - just like in the Blocks field. |\n| **`TreeViewFeature`** | No | Adds a debug box under the editor, which allows you to see the current editor state live, the dom, as well as time travel. Very useful for debugging |\n| **`EXPERIMENTAL_TableFeature`** | No | Adds support for tables. This feature may be removed or receive breaking changes in the future - even within a stable lexical release, without needing a major release. |\n\nNotice how even the toolbars are features? That's how extensible our lexical editor is - you could theoretically create your own toolbar if you wanted to!\n\n## Creating your own, custom Feature\n\nYou can find more information about creating your own feature in our [building custom feature docs](../lexical/building-custom-features).\n\n## TypeScript\n\nEvery single piece of saved data is 100% fully-typed within lexical. It provides a type for every single node, which can be imported from `@payloadcms/richtext-lexical` - each type is prefixed with `Serialized`, e.g. `SerializedUploadNode`.\n\nIn order to fully type the entire editor JSON, you can use our `TypedEditorState` helper type, which accepts a union of all possible node types as a generic. The reason we do not provide a type which already contains all possible node types is because the possible node types depend on which features you have enabled in your editor. Here is an example:\n\n```ts\nimport type {\n SerializedAutoLinkNode,\n SerializedBlockNode,\n SerializedHorizontalRuleNode,\n SerializedLinkNode,\n SerializedListItemNode,\n SerializedListNode,\n SerializedParagraphNode,\n SerializedQuoteNode,\n SerializedRelationshipNode,\n SerializedTextNode,\n SerializedUploadNode,\n TypedEditorState,\n SerializedHeadingNode,\n} from '@payloadcms/richtext-lexical'\n\nconst editorState: TypedEditorState\u003c\n | SerializedAutoLinkNode\n | SerializedBlockNode\n | SerializedHorizontalRuleNode\n | SerializedLinkNode\n | SerializedListItemNode\n | SerializedListNode\n | SerializedParagraphNode\n | SerializedQuoteNode\n | SerializedRelationshipNode\n | SerializedTextNode\n | SerializedUploadNode\n | SerializedHeadingNode\n\u003e = {\n root: {\n type: 'root',\n direction: 'ltr',\n format: '',\n indent: 0,\n version: 1,\n children: [\n {\n children: [\n {\n detail: 0,\n format: 0,\n mode: 'normal',\n style: '',\n text: 'Some text. Every property here is fully-typed',\n type: 'text',\n version: 1,\n },\n ],\n direction: 'ltr',\n format: '',\n indent: 0,\n type: 'paragraph',\n textFormat: 0,\n version: 1,\n },\n ],\n },\n}\n```\n\nAlternatively, you can use the `DefaultTypedEditorState` type, which includes all types for all nodes included in the `defaultFeatures`:\n\n```ts\nimport type {\n DefaultTypedEditorState\n} from '@payloadcms/richtext-lexical'\n\nconst editorState: DefaultTypedEditorState = {\n root: {\n type: 'root',\n direction: 'ltr',\n format: '',\n indent: 0,\n version: 1,\n children: [\n {\n children: [\n {\n detail: 0,\n format: 0,\n mode: 'normal',\n style: '',\n text: 'Some text. Every property here is fully-typed',\n type: 'text',\n version: 1,\n },\n ],\n direction: 'ltr',\n format: '',\n indent: 0,\n type: 'paragraph',\n textFormat: 0,\n version: 1,\n },\n ],\n },\n}\n```\n\nJust like `TypedEditorState`, the `DefaultTypedEditorState` also accepts an optional node type union as a generic. Here, this would **add** the specified node types to the default ones. Example: `DefaultTypedEditorState\u003cSerializedBlockNode | YourCustomSerializedNode\u003e`.\n\nThis is a type-safe representation of the editor state. Looking at the auto-suggestions of `type` it will show you all the possible node types you can use.\n\nMake sure to only use types exported from `@payloadcms/richtext-lexical`, not from the lexical core packages. We only have control over types we export and can guarantee that those are correct, even though lexical core may export types with identical names.\n\n### Automatic type generation\n\nLexical does not generate the accurate type definitions for your richText fields for you yet - this will be improved in the future. Currently, it only outputs the rough shape of the editor JSON which you can enhance using type assertions.\n"])</script><script>self.__next_f.push([1,"84:T3ff3,"])</script><script>self.__next_f.push([1,"\nLexical saves data in JSON - this is great for storage and flexibility and allows you to easily to convert it to other formats like JSX, HTML or Markdown.\n\n## Lexical =\u003e JSX\n\nIf you have a React-based frontend, converting lexical to JSX is the recommended way to render rich text content in your frontend. To do that, import the `RichText` component from `@payloadcms/richtext-lexical/react` and pass the lexical content to it:\n\n```tsx\nimport React from 'react'\nimport { RichText } from '@payloadcms/richtext-lexical/react'\n\nexport const MyComponent = ({ lexicalData }) =\u003e {\n return (\n \u003cRichText data={lexicalData} /\u003e\n )\n}\n```\n\nThe `RichText` component will come with the most common serializers built-in, though you can also pass in your own serializers if you need to.\n\n\u003cBanner type=\"default\"\u003e\n The JSX converter expects the input data to be fully populated. When fetching data, ensure the `depth` setting is high enough, to ensure that lexical nodes such as uploads are populated.\n\u003c/Banner\u003e\n\n### Converting Lexical Blocks to JSX\n\nIn order to convert lexical blocks or inline blocks to JSX, you will have to pass the converter for your block to the RichText component. This converter is not included by default, as Payload doesn't know how to render your custom blocks.\n\n```tsx\nimport React from 'react'\nimport {\n type JSXConvertersFunction,\n RichText,\n} from '@payloadcms/richtext-lexical/react'\nimport type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'\n\nconst jsxConverters: JSXConvertersFunction = ({ defaultConverters }) =\u003e ({\n ...defaultConverters,\n blocks: {\n // myTextBlock is the slug of the block\n myTextBlock: ({ node }) =\u003e \u003cdiv style={{ backgroundColor: 'red' }}\u003e{node.fields.text}\u003c/div\u003e,\n },\n})\n\nexport const MyComponent = ({ lexicalData }) =\u003e {\n return (\n \u003cRichText\n converters={jsxConverters}\n data={lexicalData.lexicalWithBlocks as SerializedEditorState}\n /\u003e\n )\n}\n```\n\n## Lexical =\u003e HTML\n\nIf you don't have a React-based frontend, or if you need to send the content to a third-party service, you can convert lexical to HTML. There are two ways to do this:\n\n1. **Outputting HTML from the Collection:** Create a new field in your collection to convert saved JSON content to HTML. Payload generates and outputs the HTML for use in your frontend.\n2. **Generating HTML on any server** Convert JSON to HTML on-demand on the server.\n\nIn both cases, the conversion needs to happen on a server, as the HTML converter will automatically fetch data for nodes that require it (e.g. uploads and internal links). The editor comes with built-in HTML serializers, simplifying the process of converting JSON to HTML.\n\n### Outputting HTML from the Collection\n\nTo add HTML generation directly within the collection, follow the example below:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nimport { HTMLConverterFeature, lexicalEditor, lexicalHTML } from '@payloadcms/richtext-lexical'\n\nconst Pages: CollectionConfig = {\n slug: 'pages',\n fields: [\n {\n name: 'nameOfYourRichTextField',\n type: 'richText',\n editor: lexicalEditor({\n features: ({ defaultFeatures }) =\u003e [\n ...defaultFeatures,\n // The HTMLConverter Feature is the feature which manages the HTML serializers.\n // If you do not pass any arguments to it, it will use the default serializers.\n HTMLConverterFeature({}),\n ],\n }),\n },\n lexicalHTML('nameOfYourRichTextField', { name: 'nameOfYourRichTextField_html' }),\n ],\n}\n```\n\nThe `lexicalHTML()` function creates a new field that automatically converts the referenced lexical richText field into HTML through an afterRead hook.\n\n### Generating HTML anywhere on the server\n\nIf you wish to convert JSON to HTML ad-hoc, use the `convertLexicalToHTML` function:\n\n```ts\nimport { consolidateHTMLConverters, convertLexicalToHTML } from '@payloadcms/richtext-lexical'\n\n\nawait convertLexicalToHTML({\n converters: consolidateHTMLConverters({ editorConfig }),\n data: editorData,\n payload, // if you have Payload but no req available, pass it in here to enable server-only functionality (e.g. proper conversion of upload nodes)\n req, // if you have req available, pass it in here to enable server-only functionality (e.g. proper conversion of upload nodes). No need to pass in Payload if req is passed in.\n})\n```\nThis method employs `convertLexicalToHTML` from `@payloadcms/richtext-lexical`, which converts the serialized editor state into HTML.\n\nBecause every `Feature` is able to provide html converters, and because the `htmlFeature` can modify those or provide their own, we need to consolidate them with the default html Converters using the `consolidateHTMLConverters` function.\n\n#### Example: Generating HTML within an afterRead hook\n\n```ts\nimport type { FieldHook } from 'payload'\n\nimport {\n HTMLConverterFeature,\n consolidateHTMLConverters,\n convertLexicalToHTML,\n defaultEditorConfig,\n defaultEditorFeatures,\n sanitizeServerEditorConfig,\n} from '@payloadcms/richtext-lexical'\n\nconst hook: FieldHook = async ({ req, siblingData }) =\u003e {\n const editorConfig = defaultEditorConfig\n\n editorConfig.features = [...defaultEditorFeatures, HTMLConverterFeature({})]\n\n const sanitizedEditorConfig = await sanitizeServerEditorConfig(editorConfig, req.payload.config)\n\n const html = await convertLexicalToHTML({\n converters: consolidateHTMLConverters({ editorConfig: sanitizedEditorConfig }),\n data: siblingData.lexicalSimple,\n req,\n })\n return html\n}\n```\n\n### CSS\n\nPayload's lexical HTML converter does not generate CSS for you, but it does add classes to the generated HTML. You can use these classes to style the HTML in your frontend.\n\nHere is some \"base\" CSS you can use to ensure that nested lists render correctly:\n\n```css\n/* Base CSS for Lexical HTML */\n.nestedListItem, .list-check {\n list-style-type: none;\n}\n```\n\n### Creating your own HTML Converter\n\nHTML Converters are typed as `HTMLConverter`, which contains the node type it should handle, and a function that accepts the serialized node from the lexical editor, and outputs the HTML string. Here's the HTML Converter of the Upload node as an example:\n\n```ts\nimport type { HTMLConverter } from '@payloadcms/richtext-lexical'\n\nconst UploadHTMLConverter: HTMLConverter\u003cSerializedUploadNode\u003e = {\n converter: async ({ node, req }) =\u003e {\n const uploadDocument: {\n value?: any\n } = {}\n if(req) {\n await populate({\n id,\n collectionSlug: node.relationTo,\n currentDepth: 0,\n data: uploadDocument,\n depth: 1,\n draft: false,\n key: 'value',\n overrideAccess: false,\n req,\n showHiddenFields: false,\n })\n }\n\n const url = (req?.payload?.config?.serverURL || '') + uploadDocument?.value?.url\n\n if (!(uploadDocument?.value?.mimeType as string)?.startsWith('image')) {\n // Only images can be serialized as HTML\n return ``\n }\n\n return `\u003cimg src=\"${url}\" alt=\"${uploadDocument?.value?.filename}\" width=\"${uploadDocument?.value?.width}\" height=\"${uploadDocument?.value?.height}\"/\u003e`\n },\n nodeTypes: [UploadNode.getType()], // This is the type of the lexical node that this converter can handle. Instead of hardcoding 'upload' we can get the node type directly from the UploadNode, since it's static.\n}\n```\n\nAs you can see, we have access to all the information saved in the node (for the Upload node, this is `value`and `relationTo`) and we can use that to generate the HTML.\n\nThe `convertLexicalToHTML` is part of `@payloadcms/richtext-lexical` automatically handles traversing the editor state and calling the correct converter for each node.\n\n### Embedding the HTML Converter in your Feature\n\nYou can embed your HTML Converter directly within your custom `ServerFeature`, allowing it to be handled automatically by the `consolidateHTMLConverters` function. Here is an example:\n\n```ts\nimport { createNode } from '@payloadcms/richtext-lexical'\nimport type { FeatureProviderProviderServer } from '@payloadcms/richtext-lexical'\n\nexport const UploadFeature: FeatureProviderProviderServer\u003c\n UploadFeatureProps,\n UploadFeaturePropsClient\n\u003e = (props) =\u003e {\n /*...*/\n return {\n feature: () =\u003e {\n return {\n nodes: [\n createNode({\n converters: {\n html: yourHTMLConverter, // \u003c= This is where you define your HTML Converter\n },\n node: UploadNode,\n //...\n }),\n ],\n ClientComponent: UploadFeatureClientComponent,\n clientFeatureProps: clientProps,\n serverFeatureProps: props,\n /*...*/\n }\n },\n key: 'upload',\n serverFeatureProps: props,\n }\n}\n```\n\n## Headless Editor\n\nLexical provides a seamless way to perform conversions between various other formats:\n\n- HTML to Lexical (or, importing HTML into the lexical editor)\n- Markdown to Lexical (or, importing Markdown into the lexical editor)\n- Lexical to Markdown\n\nA headless editor can perform such conversions outside of the main editor instance. Follow this method to initiate a headless editor:\n\n```ts\nimport { createHeadlessEditor } from '@payloadcms/richtext-lexical/lexical/headless'\nimport { getEnabledNodes, sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'\n\nconst yourEditorConfig // \u003c= your editor config here\nconst payloadConfig // \u003c= your Payload Config here\n\nconst headlessEditor = createHeadlessEditor({\n nodes: getEnabledNodes({\n editorConfig: sanitizeServerEditorConfig(yourEditorConfig, payloadConfig),\n }),\n})\n```\n\n### Getting the editor config\n\nAs you can see, you need to provide an editor config in order to create a headless editor. This is because the editor config is used to determine which nodes \u0026 features are enabled, and which converters are used.\n\nTo get the editor config, simply import the default editor config and adjust it - just like you did inside of the `editor: lexicalEditor({})` property:\n\n```ts\nimport { defaultEditorConfig, defaultEditorFeatures } from '@payloadcms/richtext-lexical' // \u003c= make sure this package is installed\n\nconst yourEditorConfig = defaultEditorConfig\n\n// If you made changes to the features of the field's editor config, you should also make those changes here:\nyourEditorConfig.features = [\n ...defaultEditorFeatures,\n // Add your custom features here\n]\n```\n\n### Getting the editor config from an existing field\n\nIf you have access to the sanitized collection config, you can get access to the lexical sanitized editor config \u0026 features, as every lexical richText field returns it. Here is an example how you can get it from another field's afterRead hook:\n\n```ts\nimport type { CollectionConfig, RichTextField } from 'payload'\nimport { createHeadlessEditor } from '@payloadcms/richtext-lexical/lexical/headless'\nimport type { LexicalRichTextAdapter, SanitizedServerEditorConfig } from '@payloadcms/richtext-lexical'\nimport {\n getEnabledNodes,\n lexicalEditor\n} from '@payloadcms/richtext-lexical'\n\nexport const MyCollection: CollectionConfig = {\n slug: 'slug',\n fields: [\n {\n name: 'text',\n type: 'text',\n hooks: {\n afterRead: [\n ({ value, collection }) =\u003e {\n const otherRichTextField: RichTextField = collection.fields.find(\n (field) =\u003e 'name' in field \u0026\u0026 field.name === 'richText',\n ) as RichTextField\n\n const lexicalAdapter: LexicalRichTextAdapter =\n otherRichTextField.editor as LexicalRichTextAdapter\n\n const sanitizedServerEditorConfig: SanitizedServerEditorConfig =\n lexicalAdapter.editorConfig\n\n const headlessEditor = createHeadlessEditor({\n nodes: getEnabledNodes({\n editorConfig: sanitizedServerEditorConfig,\n }),\n })\n\n // Do whatever you want with the headless editor\n\n return value\n },\n ],\n },\n },\n {\n name: 'richText',\n type: 'richText',\n editor: lexicalEditor({\n features,\n }),\n }\n ]\n}\n```\n\n## HTML =\u003e Lexical\n\nOnce you have your headless editor instance, you can use it to convert HTML to Lexical:\n\n```ts\nimport { $generateNodesFromDOM } from '@payloadcms/richtext-lexical/lexical/html'\nimport { $getRoot, $getSelection } from '@payloadcms/richtext-lexical/lexical'\nimport { JSDOM } from 'jsdom'\n\nheadlessEditor.update(\n () =\u003e {\n // In a headless environment you can use a package such as JSDom to parse the HTML string.\n const dom = new JSDOM(htmlString)\n\n // Once you have the DOM instance it's easy to generate LexicalNodes.\n const nodes = $generateNodesFromDOM(headlessEditor, dom.window.document)\n\n // Select the root\n $getRoot().select()\n\n // Insert them at a selection.\n const selection = $getSelection()\n selection.insertNodes(nodes)\n },\n { discrete: true },\n)\n\n// Do this if you then want to get the editor JSON\nconst editorJSON = headlessEditor.getEditorState().toJSON()\n```\n\nFunctions prefixed with a `$` can only be run inside an `editor.update()` or `editorState.read()` callback.\n\nThis has been taken from the [lexical serialization \u0026 deserialization docs](https://lexical.dev/docs/concepts/serialization#html---lexical).\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n Using the \u003ccode\u003ediscrete: true\u003c/code\u003e flag ensures instant updates to the editor state. If\n immediate reading of the updated state isn't necessary, you can omit the flag.\n\u003c/Banner\u003e\n\n## Markdown =\u003e Lexical\n\nConvert markdown content to the Lexical editor format with the following:\n\n```ts\nimport { sanitizeServerEditorConfig, $convertFromMarkdownString } from '@payloadcms/richtext-lexical'\n\nconst yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig, payloadConfig) // \u003c= your editor config \u0026 Payload Config here\nconst markdown = `# Hello World`\n\nheadlessEditor.update(\n () =\u003e {\n $convertFromMarkdownString(markdown, yourSanitizedEditorConfig.features.markdownTransformers)\n },\n { discrete: true },\n)\n\n// Do this if you then want to get the editor JSON\nconst editorJSON = headlessEditor.getEditorState().toJSON()\n```\n\n## Lexical =\u003e Markdown\n\nExport content from the Lexical editor into Markdown format using these steps:\n\n1. Import your current editor state into the headless editor.\n2. Convert and fetch the resulting markdown string.\n\nHere's the code for it:\n\n```ts\nimport { $convertToMarkdownString } from '@payloadcms/richtext-lexical/lexical/markdown'\nimport { sanitizeServerEditorConfig } from '@payloadcms/richtext-lexical'\nimport type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'\n\nconst yourSanitizedEditorConfig = sanitizeServerEditorConfig(yourEditorConfig, payloadConfig) // \u003c= your editor config \u0026 Payload Config here\nconst yourEditorState: SerializedEditorState // \u003c= your current editor state here\n\n// Import editor state into your headless editor\ntry {\n headlessEditor.update(\n () =\u003e {\n headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState))\n },\n { discrete: true }, // This should commit the editor state immediately\n )\n} catch (e) {\n logger.error({ err: e }, 'ERROR parsing editor state')\n}\n\n// Export to markdown\nlet markdown: string\nheadlessEditor.getEditorState().read(() =\u003e {\n markdown = $convertToMarkdownString(yourSanitizedEditorConfig?.features?.markdownTransformers)\n})\n```\n\n## Lexical =\u003e Plain Text\n\nExport content from the Lexical editor into plain text using these steps:\n\n1. Import your current editor state into the headless editor.\n2. Convert and fetch the resulting plain text string.\n\nHere's the code for it:\n\n```ts\nimport type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'\nimport { $getRoot } from '@payloadcms/richtext-lexical/lexical'\n\nconst yourEditorState: SerializedEditorState // \u003c= your current editor state here\n\n// Import editor state into your headless editor\ntry {\n headlessEditor.update(\n () =\u003e {\n headlessEditor.setEditorState(headlessEditor.parseEditorState(yourEditorState))\n },\n { discrete: true }, // This should commit the editor state immediately\n )\n } catch (e) {\n logger.error({ err: e }, 'ERROR parsing editor state')\n}\n\n// Export to plain text\nconst plainTextContent =\n headlessEditor.getEditorState().read(() =\u003e {\n return $getRoot().getTextContent()\n }) || ''\n```\n"])</script><script>self.__next_f.push([1,"85:T1946,"])</script><script>self.__next_f.push([1,"\n## Migrating from Slate\n\nWhile both Slate and Lexical save the editor state in JSON, the structure of the JSON is different.\n\n### Migration via Migration Script (Recommended)\n\nJust import the `migrateSlateToLexical` function we provide, pass it the `payload` object and run it. Depending on the amount of collections, this might take a while.\n\nIMPORTANT: This will overwrite all slate data. We recommend doing the following first:\n1. Take a backup of your entire database. If anything goes wrong and you do not have a backup, you are on your own and will not receive any support.\n2. Make every richText field a lexical editor. This script will only convert lexical richText fields with old Slate data\n3. Add the SlateToLexicalFeature (as seen below) first, and test it out by loading up the Admin Panel, to see if the migrator works as expected. You might have to build some custom converters for some fields first in order to convert custom Slate nodes. The SlateToLexicalFeature is where the converters are stored. Only fields with this feature added will be migrated.\n4. If this works as expected, add the `disableHooks: true` prop everywhere you're initializing `SlateToLexicalFeature`. Example: `SlateToLexicalFeature({ disableHooks: true })`. Once you did that, you're ready to run the migration script.\n\n```ts\nimport { migrateSlateToLexical } from '@payloadcms/richtext-lexical/migrate'\n\nawait migrateSlateToLexical({ payload })\n```\n\n### Migration via SlateToLexicalFeature\n\nOne way to handle this is to just give your lexical editor the ability to read the slate JSON.\n\nSimply add the `SlateToLexicalFeature` to your editor:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nimport { SlateToLexicalFeature } from '@payloadcms/richtext-lexical/migrate'\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\n\nconst Pages: CollectionConfig = {\n slug: 'pages',\n fields: [\n {\n name: 'nameOfYourRichTextField',\n type: 'richText',\n editor: lexicalEditor({\n features: ({ defaultFeatures }) =\u003e [...defaultFeatures, SlateToLexicalFeature({})],\n }),\n },\n ],\n}\n```\n\nand done! Now, every time this lexical editor is initialized, it converts the slate date to lexical on-the-fly. If the data is already in lexical format, it will just pass it through.\n\nThis is by far the easiest way to migrate from Slate to Lexical, although it does come with a few caveats:\n\n- There is a performance hit when initializing the lexical editor\n- The editor will still output the Slate data in the output JSON, as the on-the-fly converter only runs for the Admin Panel\n\nThe easy way to solve this: Edit the richText field and save the document! This overrides the slate data with the lexical data, and the next time the document is loaded, the lexical data will be used. This solves both the performance and the output issue for that specific document. This, however, is a slow and gradual migration process, thus you will have to support both API formats. Especially for a large number of documents, we recommend running the migration script, as explained above.\n\n### Converting custom Slate nodes\n\nIf you have custom Slate nodes, create a custom converter for them. Here's the Upload converter as an example:\n\n```ts\nimport type { SerializedUploadNode } from '../uploadNode'\nimport type { SlateNodeConverter } from '@payloadcms/richtext-lexical/migrate'\n\nexport const SlateUploadConverter: SlateNodeConverter = {\n converter({ slateNode }) {\n return {\n fields: {\n ...slateNode.fields,\n },\n format: '',\n relationTo: slateNode.relationTo,\n type: 'upload',\n value: {\n id: slateNode.value?.id || '',\n },\n version: 1,\n } as const as SerializedUploadNode\n },\n nodeTypes: ['upload'],\n}\n```\n\nIt's pretty simple: You get a Slate node as input, and you return the lexical node. The `nodeTypes` array is used to determine which Slate nodes this converter can handle.\n\nWhen using a migration script, you can add your custom converters to the `converters` property of the `convertSlateToLexical` props, as seen in the example above\n\nWhen using the `SlateToLexicalFeature`, you can add your custom converters to the `converters` property of the `SlateToLexicalFeature` props:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nimport { lexicalEditor } from '@payloadcms/richtext-lexical'\nimport {\n SlateToLexicalFeature,\n defaultSlateConverters,\n} from '@payloadcms/richtext-lexical'\n\nimport { YourCustomConverter } from '../converters/YourCustomConverter'\n\nconst Pages: CollectionConfig = {\n slug: 'pages',\n fields: [\n {\n name: 'nameOfYourRichTextField',\n type: 'richText',\n editor: lexicalEditor({\n features: ({ defaultFeatures }) =\u003e [\n ...defaultFeatures,\n SlateToLexicalFeature({\n converters: [...defaultSlateConverters, YourCustomConverter],\n }),\n ],\n }),\n },\n ],\n}\n```\n\n## Migrating from payload-plugin-lexical\n\nMigrating from [payload-plugin-lexical](https://github.com/AlessioGr/payload-plugin-lexical) works similar to migrating from Slate.\n\nInstead of a `SlateToLexicalFeature` there is a `LexicalPluginToLexicalFeature` you can use. And instead of `convertSlateToLexical` you can use `convertLexicalPluginToLexical`.\n\n## Migrating lexical data from old version to new version\n\nEach lexical node has a `version` property which is saved in the database. Every time we make a breaking change to the node's data, we increment the version. This way, we can detect an old version and automatically convert old data to the new format once you open up the editor.\n\nThe problem is, this migration only happens when you open the editor, modify the richText field (so that the field's `setValue` function is called) and save the document. Until you do that for all documents, some documents will still have the old data.\n\nTo solve this, we export an `upgradeLexicalData` function which goes through every single document in your Payload app and re-saves it, if it has a lexical editor. This way, the data is automatically converted to the new format, and that automatic conversion gets applied to every single document in your app.\n\nIMPORTANT: Take a backup of your entire database. If anything goes wrong and you do not have a backup, you are on your own and will not receive any support.\n\n```ts\nimport { upgradeLexicalData } from '@payloadcms/richtext-lexical'\n\nawait upgradeLexicalData({ payload })\n```\n"])</script><script>self.__next_f.push([1,"86:T9a22,"])</script><script>self.__next_f.push([1,"\nBefore you begin building custom features for Lexical, it is crucial to familiarize yourself with the [Lexical docs](https://lexical.dev/docs/intro), particularly the \"Concepts\" section. This foundation is necessary for understanding Lexical's core principles, such as nodes, editor state, and commands.\n\nLexical features are designed to be modular, meaning each piece of functionality is encapsulated within just two specific interfaces: one for server-side code and one for client-side code.\n\nBy convention, these are named feature.server.ts for server-side functionality and feature.client.ts for client-side functionality. The primary functionality is housed within feature.server.ts, which users will import into their projects. The client-side feature, although defined separately, is integrated and rendered server-side through the server feature. That way, we still maintain a clear boundary between server and client code, while also centralizing the code needed for a feature in basically one place. This approach is beneficial for managing all the bits and pieces which make up your feature as a whole, such as toolbar entries, buttons, or new nodes, allowing each feature to be neatly contained and managed independently.\n\n\n## Server Feature\n\nTo start building new features, you should start with the server feature, which is the entry-point.\n\n**Example myFeature/feature.server.ts:**\n\n```ts\nimport { createServerFeature } from '@payloadcms/richtext-lexical';\n\nexport const MyFeature = createServerFeature({\n feature: {\n },\n key: 'myFeature',\n})\n```\n\n`createServerFeature` is a helper function which lets you create new features without boilerplate code.\n\nNow, the feature is ready to be used in the editor:\n\n```ts\nimport { MyFeature } from './myFeature/feature.server';\nimport { lexicalEditor } from '@payloadcms/richtext-lexical';\n\n//...\n {\n name: 'richText',\n type: 'richText',\n editor: lexicalEditor({\n features: [\n MyFeature(),\n ],\n }),\n },\n```\n\nBy default, this server feature does nothing - you haven't added any functionality yet. Depending on what you want your\nfeature to do, the ServerFeature type exposes various properties you can set to inject custom functionality into the lexical editor.\n\n### i18n\n\nEach feature can register their own translations, which are automatically scoped to the feature key:\n\n```ts\nimport { createServerFeature } from '@payloadcms/richtext-lexical';\n\n\nexport const MyFeature = createServerFeature({\n feature: {\n i18n: {\n en: {\n label: 'My Feature',\n },\n de: {\n label: 'Mein Feature',\n },\n },\n },\n key: 'myFeature',\n})\n```\n\nThis allows you to add i18n translations scoped to your feature. This specific example translation will be available under `lexical:myFeature:label` - `myFeature` being your feature key.\n\n### Markdown Transformers#server-feature-markdown-transformers\n\nThe Server Feature, just like the Client Feature, allows you to add markdown transformers. Markdown transformers on the server are used when [converting the editor from or to markdown](../lexical/converters#markdown-lexical).\n\n```ts\nimport { createServerFeature } from '@payloadcms/richtext-lexical';\nimport type { ElementTransformer } from '@payloadcms/richtext-lexical/lexical/markdown'\nimport {\n $createMyNode,\n $isMyNode,\n MyNode\n} from './nodes/MyNode'\n\nconst MyMarkdownTransformer: ElementTransformer = {\n type: 'element',\n dependencies: [MyNode],\n export: (node, exportChildren) =\u003e {\n if (!$isMyNode(node)) {\n return null\n }\n return '+++'\n },\n // match ---\n regExp: /^+++\\s*$/,\n replace: (parentNode) =\u003e {\n const node = $createMyNode()\n if (node) {\n parentNode.replace(node)\n }\n },\n}\n\n\nexport const MyFeature = createServerFeature({\n feature: {\n markdownTransformers: [MyMarkdownTransformer],\n },\n key: 'myFeature',\n})\n```\n\nIn this example, the node will be outputted as `+++` in Markdown, and the markdown `+++` will be converted to a `MyNode` node in the editor.\n\n### Nodes#server-feature-nodes\n\nWhile nodes added to the server feature do not control how the node is rendered in the editor, they control other aspects of the node:\n- HTML conversion\n- Node Hooks\n- Sub fields\n- Behavior in a headless editor\n\nThe `createNode` helper function is used to create nodes with proper typing. It is recommended to use this function to create nodes.\n\n```ts\nimport { createServerFeature, createNode } from '@payloadcms/richtext-lexical';\nimport {\n MyNode\n} from './nodes/MyNode'\n\nexport const MyFeature = createServerFeature({\n feature: {\n\n nodes: [\n // Use the createNode helper function to more easily create nodes with proper typing\n createNode({\n converters: {\n html: {\n converter: () =\u003e {\n return `\u003chr/\u003e`\n },\n nodeTypes: [MyNode.getType()],\n },\n },\n // Here you can add your actual node. On the server, they will be\n // used to initialize a headless editor which can be used to perform\n // operations on the editor, like markdown / html conversion.\n node: MyNode,\n }),\n ],\n },\n key: 'myFeature',\n})\n```\n\nWhile nodes in the client feature are added by themselves to the nodes array, nodes in the server feature can be added together with the following sibling options:\n\n| Option | Description |\n|---------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`getSubFields`** | If a node includes sub-fields (e.g. block and link nodes), passing the subFields schema here will make Payload automatically populate \u0026 run hooks for them. |\n| **`getSubFieldsData`** | If a node includes sub-fields, the sub-fields data needs to be returned here, alongside `getSubFields` which returns their schema. |\n| **`graphQLPopulationPromises`** | Allows you to run population logic when a node's data was requested from GraphQL. While `getSubFields` and `getSubFieldsData` automatically handle populating sub-fields (since they run hooks on them), those are only populated in the Rest API. This is because the Rest API hooks do not have access to the 'depth' property provided by GraphQL. In order for them to be populated correctly in GraphQL, the population logic needs to be provided here. |\n| **`node`** | The actual lexical node needs to be provided here. This also supports [lexical node replacements](https://lexical.dev/docs/concepts/node-replacement). |\n| **`validations`** | This allows you to provide node validations, which are run when your document is being validated, alongside other Payload fields. You can use it to throw a validation error for a specific node in case its data is incorrect. |\n| **`converters`** | Allows you to define how a node can be serialized into different formats. Currently, only supports HTML. Markdown converters are defined in `markdownTransformers` and not here. |\n| **`hooks`** | Just like Payload fields, you can provide hooks which are run for this specific node. These are called Node Hooks. |\n\n### Feature load order\n\nServer features can also accept a function as the `feature` property (useful for sanitizing props, as mentioned below). This function will be called when the feature is loaded during the Payload sanitization process:\n\n```ts\nimport { createServerFeature } from '@payloadcms/richtext-lexical';\n\ncreateServerFeature({\n //...\n feature: async ({ config, isRoot, props, resolvedFeatures, unSanitizedEditorConfig, featureProviderMap }) =\u003e {\n\n return {\n //Actual server feature here...\n }\n }\n})\n```\n\n\"Loading\" here means the process of calling this `feature` function. By default, features are called in the order in which they are added to the editor.\nHowever, sometimes you might want to load a feature after another feature has been loaded, or require a different feature to be loaded, throwing an error if this is not the case.\n\nWithin lexical, one example where this is done are our list features. Both `UnorderedListFeature` and `OrderedListFeature` register the same `ListItem` node. Within `UnorderedListFeature` we register it normally, but within `OrderedListFeature` we want to only register the `ListItem` node if the `UnorderedListFeature` is not present - otherwise, we would have two features registering the same node.\n\nHere is how we do it:\n\n```ts\nimport { createServerFeature, createNode } from '@payloadcms/richtext-lexical';\n\nexport const OrderedListFeature = createServerFeature({\n feature: ({ featureProviderMap }) =\u003e {\n return {\n // ...\n nodes: featureProviderMap.has('unorderedList')\n ? []\n : [\n createNode({\n // ...\n }),\n ],\n }\n },\n key: 'orderedList',\n})\n```\n\n`featureProviderMap` will always be available and contain all the features, even yet-to-be-loaded ones, so we can check if a feature is loaded by checking if its `key` present in the map.\n\nIf you wanted to make sure a feature is loaded before another feature, you can use the `dependenciesPriority` property:\n\n```ts\nimport { createServerFeature } from '@payloadcms/richtext-lexical';\n\nexport const MyFeature = createServerFeature({\n feature: ({ featureProviderMap }) =\u003e {\n return {\n // ...\n }\n },\n key: 'myFeature',\n dependenciesPriority: ['otherFeature'],\n})\n```\n\n| Option | Description |\n|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`dependenciesSoft`** | Keys of soft-dependencies needed for this feature. These are optional. Payload will attempt to load them before this feature, but doesn't throw an error if that's not possible. |\n| **`dependencies`** | Keys of dependencies needed for this feature. These dependencies do not have to be loaded first, but they have to exist, otherwise an error will be thrown. |\n| **`dependenciesPriority`** | Keys of priority dependencies needed for this feature. These dependencies have to be loaded first AND have to exist, otherwise an error will be thrown. They will be available in the `feature` property. |\n\n## Client Feature\n\nMost of the functionality which the user actually sees and interacts with, like toolbar items and React components for nodes, resides on the client-side.\n\nTo set up your client-side feature, follow these three steps:\n\n1. **Create a Separate File**: Start by creating a new file specifically for your client feature, such as `myFeature/feature.client.ts`. It's important to keep client and server features in separate files to maintain a clean boundary between server and client code.\n2. **'use client'**: Mark that file with a 'use client' directive at the top of the file\n3. **Register the Client Feature**: Register the client feature within your server feature, by passing it to the `ClientFeature` prop. This is needed because the server feature is the sole entry-point of your feature. This also means you are not able to create a client feature without a server feature, as you will not be able to register it otherwise.\n\n**Example myFeature/feature.client.ts:**\n\n```ts\n'use client'\n\nimport { createClientFeature } from '@payloadcms/richtext-lexical/client';\n\nexport const MyClientFeature = createClientFeature({\n\n})\n```\n\nExplore the APIs available through ClientFeature to add the specific functionality you need. Remember, do not import directly from `'@payloadcms/richtext-lexical'` when working on the client-side, as it will cause errors with webpack or turbopack. Instead, use `'@payloadcms/richtext-lexical/client'` for all client-side imports. Type-imports are excluded from this rule and can always be imported.\n\n### Nodes#client-feature-nodes\n\nAdd nodes to the `nodes` array in **both** your client \u0026 server feature. On the server side, nodes are utilized for backend operations like HTML conversion in a headless editor. On the client side, these nodes are integral to how content is displayed and managed in the editor, influencing how they are rendered, behave, and saved in the database.\n\nExample:\n\n**myFeature/feature.client.ts:**\n\n```ts\n'use client'\n\nimport { createClientFeature } from '@payloadcms/richtext-lexical/client';\nimport { MyNode } from './nodes/MyNode';\n\nexport const MyClientFeature = createClientFeature({\n nodes: [MyNode]\n})\n```\n\nThis also supports [lexical node replacements](https://lexical.dev/docs/concepts/node-replacement).\n\n**myFeature/nodes/MyNode.tsx:**\n\nHere is a basic DecoratorNode example:\n\n```ts\nimport type {\n DOMConversionMap,\n DOMConversionOutput,\n DOMExportOutput,\n EditorConfig,\n LexicalNode,\n SerializedLexicalNode,\n} from '@payloadcms/richtext-lexical/lexical'\n\nimport { $applyNodeReplacement, DecoratorNode } from '@payloadcms/richtext-lexical/lexical'\n\n// SerializedLexicalNode is the default lexical node.\n// By setting your SerializedMyNode type to SerializedLexicalNode,\n// you are basically saying that this node does not save any additional data.\n// If you want your node to save data, feel free to extend it\nexport type SerializedMyNode = SerializedLexicalNode\n\n// Lazy-import the React component to your node here\nconst MyNodeComponent = React.lazy(() =\u003e\n import('../component/index.js').then((module) =\u003e ({\n default: module.MyNodeComponent,\n })),\n)\n\n/**\n * This node is a DecoratorNode. DecoratorNodes allow you to render React components in the editor.\n *\n * They need both createDom and decorate functions. createDom =\u003e outside of the html. decorate =\u003e React Component inside of the html.\n *\n * If we used DecoratorBlockNode instead, we would only need a decorate method\n */\nexport class MyNode extends DecoratorNode\u003cReact.ReactElement\u003e {\n static clone(node: MyNode): MyNode {\n return new MyNode(node.__key)\n }\n\n static getType(): string {\n return 'myNode'\n }\n\n /**\n * Defines what happens if you copy a div element from another page and paste it into the lexical editor\n *\n * This also determines the behavior of lexical's internal HTML -\u003e Lexical converter\n */\n static importDOM(): DOMConversionMap | null {\n return {\n div: () =\u003e ({\n conversion: $yourConversionMethod,\n priority: 0,\n }),\n }\n }\n\n /**\n * The data for this node is stored serialized as JSON. This is the \"load function\" of that node: it takes the saved data and converts it into a node.\n */\n static importJSON(serializedNode: SerializedMyNode): MyNode {\n return $createMyNode()\n }\n\n /**\n * Determines how the hr element is rendered in the lexical editor. This is only the \"initial\" / \"outer\" HTML element.\n */\n createDOM(config: EditorConfig): HTMLElement {\n const element = document.createElement('div')\n return element\n }\n\n /**\n * Allows you to render a React component within whatever createDOM returns.\n */\n decorate(): React.ReactElement {\n return \u003cMyNodeComponent nodeKey={this.__key} /\u003e\n }\n\n /**\n * Opposite of importDOM, this function defines what happens when you copy a div element from the lexical editor and paste it into another page.\n *\n * This also determines the behavior of lexical's internal Lexical -\u003e HTML converter\n */\n exportDOM(): DOMExportOutput {\n return { element: document.createElement('div') }\n }\n /**\n * Opposite of importJSON. This determines what data is saved in the database / in the lexical editor state.\n */\n exportJSON(): SerializedLexicalNode {\n return {\n type: 'myNode',\n version: 1,\n }\n }\n\n getTextContent(): string {\n return '\\n'\n }\n\n isInline(): false {\n return false\n }\n\n updateDOM(): boolean {\n return false\n }\n}\n\n// This is used in the importDOM method. Totally optional if you do not want your node to be created automatically when copy \u0026 pasting certain dom elements\n// into your editor.\nfunction $yourConversionMethod(): DOMConversionOutput {\n return { node: $createMyNode() }\n}\n\n// This is a utility method to create a new MyNode. Utility methods prefixed with $ make it explicit that this should only be used within lexical\nexport function $createMyNode(): MyNode {\n return $applyNodeReplacement(new MyNode())\n}\n\n// This is just a utility method you can use to check if a node is a MyNode. This also ensures correct typing.\nexport function $isMyNode(\n node: LexicalNode | null | undefined,\n): node is MyNode {\n return node instanceof MyNode\n}\n```\n\nPlease do not add any 'use client' directives to your nodes, as the node class can be used on the server.\n\n### Plugins\n\nOne small part of a feature are plugins. The name stems from the lexical playground plugins and is just a small part of a lexical feature.\nPlugins are simply React components which are added to the editor, within all the lexical context providers. They can be used to add any functionality\nto the editor, by utilizing the lexical API.\n\nMost commonly, they are used to register [lexical listeners](https://lexical.dev/docs/concepts/listeners), [node transforms](https://lexical.dev/docs/concepts/transforms) or [commands](https://lexical.dev/docs/concepts/commands).\nFor example, you could add a drawer to your plugin and register a command which opens it. That command can then be called from anywhere within lexical, e.g. from within your custom lexical node.\n\nTo add a plugin, simply add it to the `plugins` array in your client feature:\n\n```ts\n'use client'\n\nimport { createClientFeature } from '@payloadcms/richtext-lexical/client';\nimport { MyPlugin } from './plugin';\n\nexport const MyClientFeature = createClientFeature({\n plugins: [MyPlugin]\n})\n```\n\nExample plugin.tsx:\n\n```ts\n'use client'\nimport type {\n LexicalCommand,\n} from '@payloadcms/richtext-lexical/lexical'\n\nimport {\n createCommand,\n $getSelection,\n $isRangeSelection,\n COMMAND_PRIORITY_EDITOR\n} from '@payloadcms/richtext-lexical/lexical'\n\nimport { useLexicalComposerContext } from '@payloadcms/richtext-lexical/lexical/react/LexicalComposerContext.js'\nimport { $insertNodeToNearestRoot } from '@payloadcms/richtext-lexical/lexical/utils'\nimport { useEffect } from 'react'\n\nimport type { PluginComponent } from '@payloadcms/richtext-lexical' // type imports can be imported from @payloadcms/richtext-lexical - even on the client\n\nimport {\n $createMyNode,\n} from '../nodes/MyNode'\nimport './index.scss'\n\nexport const INSERT_MYNODE_COMMAND: LexicalCommand\u003cvoid\u003e = createCommand(\n 'INSERT_MYNODE_COMMAND',\n)\n\n/**\n * Plugin which registers a lexical command to insert a new MyNode into the editor\n */\nexport const MyNodePlugin: PluginComponent= () =\u003e {\n // The useLexicalComposerContext hook can be used to access the lexical editor instance\n const [editor] = useLexicalComposerContext()\n\n useEffect(() =\u003e {\n return editor.registerCommand(\n INSERT_MYNODE_COMMAND,\n (type) =\u003e {\n const selection = $getSelection()\n\n if (!$isRangeSelection(selection)) {\n return false\n }\n\n const focusNode = selection.focus.getNode()\n\n if (focusNode !== null) {\n const newMyNode = $createMyNode()\n $insertNodeToNearestRoot(newMyNode)\n }\n\n return true\n },\n COMMAND_PRIORITY_EDITOR,\n )\n }, [editor])\n\n return null\n}\n```\n\nIn this example, we register a lexical command, which simply inserts a new MyNode into the editor. This command can be called from anywhere within lexical, e.g. from within a custom node.\n\n### Toolbar groups\n\nToolbar groups are visual containers which hold toolbar items. There are different toolbar group types which determine *how* a toolbar item is displayed: `dropdown` and `buttons`.\n\nAll the default toolbar groups are exported from `@payloadcms/richtext-lexical/client`. You can use them to add your own toolbar items to the editor:\n- Dropdown: `toolbarAddDropdownGroupWithItems`\n- Dropdown: `toolbarTextDropdownGroupWithItems`\n- Buttons: `toolbarFormatGroupWithItems`\n- Buttons: `toolbarFeatureButtonsGroupWithItems`\n\nWithin dropdown groups, items are positioned vertically when the dropdown is opened and include the icon \u0026 label. Within button groups, items are positioned horizontally and only include the icon. If a toolbar group with the same key is declared twice, all its items will be merged into one group.\n\n#### Custom buttons toolbar group\n\n| Option | Description |\n|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`items`** | All toolbar items part of this toolbar group need to be added here. |\n| **`key`** | Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together. |\n| **`order`** | Determines where the toolbar group will be. |\n| **`type`** | Controls the toolbar group type. Set to `buttons` to create a buttons toolbar group, which displays toolbar items horizontally using only their icons. |\n\nExample:\n```ts\nimport type { ToolbarGroup, ToolbarGroupItem } from '@payloadcms/richtext-lexical'\n\nexport const toolbarFormatGroupWithItems = (items: ToolbarGroupItem[]): ToolbarGroup =\u003e {\n return {\n type: 'buttons',\n items,\n key: 'myButtonsToolbar',\n order: 10,\n }\n}\n```\n\n#### Custom dropdown toolbar group\n\n| Option | Description |\n|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`items`** | All toolbar items part of this toolbar group need to be added here. |\n| **`key`** | Each toolbar group needs to have a unique key. Groups with the same keys will have their items merged together. |\n| **`order`** | Determines where the toolbar group will be. |\n| **`type`** | Controls the toolbar group type. Set to `dropdown` to create a buttons toolbar group, which displays toolbar items vertically using their icons and labels, if the dropdown is open. |\n| **`ChildComponent`** | The dropdown toolbar ChildComponent allows you to pass in a React Component which will be displayed within the dropdown button. |\n\nExample:\n```ts\nimport type { ToolbarGroup, ToolbarGroupItem } from '@payloadcms/richtext-lexical'\n\nimport { MyIcon } from './icons/MyIcon'\n\nexport const toolbarAddDropdownGroupWithItems = (items: ToolbarGroupItem[]): ToolbarGroup =\u003e {\n return {\n type: 'dropdown',\n ChildComponent: MyIcon,\n items,\n key: 'myDropdownToolbar',\n order: 10,\n }\n}\n```\n\n### Toolbar items\n\nCustom nodes and features on its own are pointless, if they can't be added to the editor. You will need to hook in one of our interfaces which allow the user to interact with the editor:\n\n- Fixed toolbar which stays fixed at the top of the editor\n- Inline, floating toolbar which appears when selecting text\n- Slash menu which appears when typing `/` in the editor\n- Markdown transformers, which are triggered when a certain text pattern is typed in the editor\n- Or any other interfaces which can be added via your own plugins. Our toolbars are a prime example of this - they are just plugins.\n\nTo add a toolbar item to either the floating or the inline toolbar, you can add a ToolbarGroup with a ToolbarItem to the `toolbarFixed` or `toolbarInline` props of your client feature:\n\n```ts\n'use client'\n\nimport { createClientFeature, toolbarAddDropdownGroupWithItems } from '@payloadcms/richtext-lexical/client';\nimport { IconComponent } from './icon';\nimport { $isHorizontalRuleNode } from './nodes/MyNode';\nimport { INSERT_MYNODE_COMMAND } from './plugin';\nimport { $isNodeSelection } from '@payloadcms/richtext-lexical/lexical'\n\nexport const MyClientFeature = createClientFeature({\n toolbarFixed: {\n groups: [\n toolbarAddDropdownGroupWithItems([\n {\n ChildComponent: IconComponent,\n isActive: ({ selection }) =\u003e {\n if (!$isNodeSelection(selection) || !selection.getNodes().length) {\n return false\n }\n\n const firstNode = selection.getNodes()[0]\n return $isHorizontalRuleNode(firstNode)\n },\n key: 'myNode',\n label: ({ i18n }) =\u003e {\n return i18n.t('lexical:myFeature:label')\n },\n onSelect: ({ editor }) =\u003e {\n editor.dispatchCommand(INSERT_MYNODE_COMMAND, undefined)\n },\n },\n ]),\n ],\n },\n})\n```\n\nYou will have to provide a toolbar group first, and then the items for that toolbar group (more on that above).\n\nA `ToolbarItem` various props you can use to customize its behavior:\n\n| Option | Description |\n|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`ChildComponent`** | A React component which is rendered within your toolbar item's default button component. Usually, you want this to be an icon. |\n| **`Component`** | A React component which is rendered in place of the toolbar item's default button component, thus completely replacing it. The `ChildComponent` and `onSelect` properties will be ignored. |\n| **`label`** | The label will be displayed in your toolbar item, if it's within a dropdown group. To make use of i18n, this can be a function. |\n| **`key`** | Each toolbar item needs to have a unique key. |\n| **`onSelect`** | A function which is called when the toolbar item is clicked. |\n| **`isEnabled`** | This is optional and controls if the toolbar item is clickable or not. If `false` is returned here, it will be grayed out and unclickable. |\n| **`isActive`** | This is optional and controls if the toolbar item is highlighted or not |\n\nThe API for adding an item to the floating inline toolbar (`toolbarInline`) is identical. If you wanted to add an item to both the fixed and inline toolbar, you can extract it into its own variable\n(typed as `ToolbarGroup[]`) and add it to both the `toolbarFixed` and `toolbarInline` props.\n\n### Slash Menu groups\n\nWe're exporting `slashMenuBasicGroupWithItems` from `@payloadcms/richtext-lexical/client` which you can use to add items to the slash menu labelled \"Basic\". If you want to create your own slash menu group, here is an example:\n\n```ts\nimport type {\n SlashMenuGroup,\n SlashMenuItem,\n} from '@payloadcms/richtext-lexical'\n\nexport function mwnSlashMenuGroupWithItems(items: SlashMenuItem[]): SlashMenuGroup {\n return {\n items,\n key: 'myGroup',\n label: 'My Group' // \u003c= This can be a function to make use of i18n\n }\n}\n```\n\nBy creating a helper function like this, you can easily re-use it and add items to it. All Slash Menu groups with the same keys will have their items merged together.\n\n| Option | Description |\n|-------------|---------------------------------------------------------------------------------------------------------------------------------------|\n| **`items`** | An array of `SlashMenuItem`'s which will be displayed in the slash menu. |\n| **`label`** | The label will be displayed before your Slash Menu group. In order to make use of i18n, this can be a function. |\n| **`key`** | Used for class names and, if label is not provided, for display. Slash menus with the same key will have their items merged together. |\n\n\n### Slash Menu items\n\nThe API for adding items to the slash menu is similar. There are slash menu groups, and each slash menu groups has items. Here is an example:\n\n```ts\n'use client'\n\nimport { createClientFeature, slashMenuBasicGroupWithItems } from '@payloadcms/richtext-lexical/client';\nimport { INSERT_MYNODE_COMMAND } from './plugin';\nimport { IconComponent } from './icon';\n\nexport const MyClientFeature = createClientFeature({\n slashMenu: {\n groups: [\n slashMenuBasicGroupWithItems([\n {\n Icon: IconComponent,\n key: 'myNode',\n keywords: ['myNode', 'myFeature', 'someOtherKeyword'],\n label: ({ i18n }) =\u003e {\n return i18n.t('lexical:myFeature:label')\n },\n onSelect: ({ editor }) =\u003e {\n editor.dispatchCommand(INSERT_MYNODE_COMMAND, undefined)\n },\n },\n ]),\n ],\n },\n})\n```\n\n| Option | Description |\n|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| **`Icon`** | The icon which is rendered in your slash menu item. |\n| **`label`** | The label will be displayed in your slash menu item. In order to make use of i18n, this can be a function. |\n| **`key`** | Each slash menu item needs to have a unique key. The key will be matched when typing, displayed if no `label` property is set, and used for classNames. |\n| **`onSelect`** | A function which is called when the slash menu item is selected. |\n| **`keywords`** | Keywords are used to match the item for different texts typed after the '/'. E.g. you might want to show a horizontal rule item if you type both /hr, /separator, /horizontal etc. In addition to the keywords, the label and key will be used to find the right slash menu item. |\n\n\n### Markdown Transformers#client-feature-markdown-transformers\n\nThe Client Feature, just like the Server Feature, allows you to add markdown transformers. Markdown transformers on the client are used to create new nodes when a certain markdown pattern is typed in the editor.\n\n```ts\nimport { createClientFeature } from '@payloadcms/richtext-lexical/client';\nimport type { ElementTransformer } from '@payloadcms/richtext-lexical/lexical/markdown'\nimport {\n $createMyNode,\n $isMyNode,\n MyNode\n} from './nodes/MyNode'\n\nconst MyMarkdownTransformer: ElementTransformer = {\n type: 'element',\n dependencies: [MyNode],\n export: (node, exportChildren) =\u003e {\n if (!$isMyNode(node)) {\n return null\n }\n return '+++'\n },\n // match ---\n regExp: /^+++\\s*$/,\n replace: (parentNode) =\u003e {\n const node = $createMyNode()\n if (node) {\n parentNode.replace(node)\n }\n },\n}\n\n\nexport const MyFeature = createClientFeature({\n markdownTransformers: [MyMarkdownTransformer],\n})\n```\n\nIn this example, a new `MyNode` will be inserted into the editor when `+++ ` is typed.\n\n### Providers\n\nYou can add providers to your client feature, which will be nested below the `EditorConfigProvider`. This can be useful if you want to provide some context to your nodes or other parts of your feature.\n\n```ts\n'use client'\n\nimport { createClientFeature } from '@payloadcms/richtext-lexical/client';\nimport { TableContext } from './context';\n\nexport const MyClientFeature = createClientFeature({\n providers: [TableContext],\n})\n```\n\n## Props\n\nTo accept props in your feature, type them as a generic.\n\nServer Feature:\n\n```ts\ncreateServerFeature\u003cUnSanitizedProps, SanitizedProps, UnSanitizedClientProps\u003e({\n //...\n})\n```\n\nClient Feature:\n\n```ts\ncreateClientFeature\u003cUnSanitizedClientProps, SanitizedClientProps\u003e({\n //...\n})\n```\n\nThe unSanitized props are what the user will pass to the feature when they call its provider function and add it to their editor config. You then have an option to sanitize those props.\nTo sanitize those in the server feature, you can pass a function to `feature` instead of an object:\n\n```ts\ncreateServerFeature\u003cUnSanitizedProps, SanitizedProps, UnSanitizedClientProps\u003e({\n //...\n feature: async ({ config, isRoot, props, resolvedFeatures, unSanitizedEditorConfig, featureProviderMap }) =\u003e {\n const sanitizedProps = doSomethingWithProps(props)\n\n return {\n sanitizedServerFeatureProps: sanitizedProps,\n //Actual server feature here...\n }\n }\n})\n```\n\nKeep in mind that any sanitized props then have to be returned in the `sanitizedServerFeatureProps` property.\n\nIn the client feature, it works similarly:\n\n```ts\ncreateClientFeature\u003cUnSanitizedClientProps, SanitizedClientProps\u003e(\n ({ clientFunctions, featureProviderMap, props, resolvedFeatures, unSanitizedEditorConfig }) =\u003e {\n const sanitizedProps = doSomethingWithProps(props)\n return {\n sanitizedClientFeatureProps: sanitizedProps,\n //Actual client feature here...\n }\n },\n)\n```\n\n### Bringing props from the server to the client\n\nBy default, the client feature will never receive any props from the server feature. In order to pass props from the server to the client, you can need to return those props in the server feature:\n\n```ts\ntype UnSanitizedClientProps = {\n test: string\n}\n\ncreateServerFeature\u003cUnSanitizedProps, SanitizedProps, UnSanitizedClientProps\u003e({\n //...\n feature: {\n clientFeatureProps: {\n test: 'myValue'\n }\n }\n})\n```\n\nThe reason the client feature does not have the same props available as the server by default is because all client props need to be serializable. You can totally accept things like functions or Maps as props in your server feature, but you will not be able to send those to the client. In the end, those props are sent from the server to the client over the network, so they need to be serializable.\n\n## More information\n\nHave a look at the [features we've already built](https://github.com/payloadcms/payload/tree/main/packages/richtext-lexical/src/features) - understanding how they work will help you understand how to create your own. There is no difference between the features included by default and the ones you create yourself - since those features are all isolated from the \"core\", you have access to the same APIs, whether the feature is part of Payload or not!\n"])</script><script>self.__next_f.push([1,"87:T1e73,"])</script><script>self.__next_f.push([1,"\nWith Live Preview you can render your front-end application directly within the [Admin Panel](../admin/overview). As you type, your changes take effect in real-time. No need to save a draft or publish your changes. This works in both [Server-side](./server) as well as [Client-side](./client) environments.\n\nLive Preview works by rendering an iframe on the page that loads your front-end application. The Admin Panel communicates with your app through [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) events. These events are emitted every time a change is made to the Document. Your app then listens for these events and re-renders itself with the data it receives.\n\nTo add Live Preview, use the `admin.livePreview` property in your [Payload Config](../configuration/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n // ...\n // highlight-start\n livePreview: {\n url: 'http://localhost:3000',\n collections: ['pages']\n },\n // highlight-end\n }\n})\n```\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n Alternatively, you can define the \u003ccode\u003eadmin.livePreview\u003c/code\u003e property on individual [Collection Admin Configs](../admin/collections) and [Global Admin Configs](../admin/globals). Settings defined here will be merged into the top-level as overrides.\n\u003c/Banner\u003e\n\n{/* IMAGE OF LIVE PREVIEW HERE */}\n\n## Options\n\nSetting up Live Preview is easy. This can be done either globally through the [Root Admin Config](../admin/overview), or on individual [Collection Admin Configs](../admin/collections) and [Global Admin Configs](../admin/globals). Once configured, a new \"Live Preview\" tab will appear at the top of enabled Documents. Navigating to this tab opens the preview window and loads your front-end application.\n\nThe following options are available:\n\n| Path | Description |\n| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`url`** \\* | String, or function that returns a string, pointing to your front-end application. This value is used as the iframe `src`. [More details](#url). |\n| **`breakpoints`** | Array of breakpoints to be used as “device sizes” in the preview window. Each item appears as an option in the toolbar. [More details](#breakpoints). |\n| **`collections`** | Array of collection slugs to enable Live Preview on. |\n| **`globals`** | Array of global slugs to enable Live Preview on. |\n\n_\\* An asterisk denotes that a property is required._\n\n### URL\n\nThe `url` property is a string that points to your front-end application. This value is used as the `src` attribute of the iframe rendering your front-end. Once loaded, the Admin Panel will communicate directly with your app through `window.postMessage` events.\n\nTo set the URL, use the `admin.livePreview.url` property in your [Payload Config](../configuration/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n // ...\n livePreview: {\n url: 'http://localhost:3000', // highlight-line\n collections: ['pages'],\n },\n }\n})\n```\n\n#### Dynamic URLs\n\nYou can also pass a function in order to dynamically format URLs. This is useful for multi-tenant applications, localization, or any other scenario where the URL needs to be generated based on the Document being edited.\n\nTo set dynamic URLs, set the `admin.livePreview.url` property in your [Payload Config](../configuration/overview) to a function:\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n // ...\n livePreview: {\n // highlight-start\n url: ({\n data,\n documentInfo,\n locale\n }) =\u003e `${data.tenant.url}${ // Multi-tenant top-level domain\n documentInfo.slug === 'posts' ? `/posts/${data.slug}` : `${data.slug !== 'home' : `/${data.slug}` : ''}`\n }${locale ? `?locale=${locale?.code}` : ''}`, // Localization query param\n collections: ['pages'],\n },\n // highlight-end\n }\n})\n```\n\nThe following arguments are provided to the `url` function:\n\n| Path | Description |\n| ------------------ | ----------------------------------------------------------------------------------------------------------------- |\n| **`data`** | The data of the Document being edited. This includes changes that have not yet been saved. |\n| **`documentInfo`** | Information about the Document being edited like collection slug. [More details](../admin/hooks#usedocumentinfo). |\n| **`locale`** | The locale currently being edited (if applicable). [More details](../configuration/localization). |\n\n### Breakpoints\n\nThe breakpoints property is an array of objects which are used as “device sizes” in the preview window. Each item will render as an option in the toolbar. When selected, the preview window will resize to the exact dimensions specified in that breakpoint.\n\nTo set breakpoints, use the `admin.livePreview.breakpoints` property in your [Payload Config](../configuration/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n admin: {\n // ...\n livePreview: {\n url: 'http://localhost:3000',\n // highlight-start\n breakpoints: [\n {\n label: 'Mobile',\n name: 'mobile',\n width: 375,\n height: 667,\n },\n ],\n // highlight-end\n },\n }\n})\n```\n\nThe following options are available for each breakpoint:\n\n| Path | Description |\n| --------------- | --------------------------------------------------------------------------- |\n| **`label`** \\* | The label to display in the drop-down. This is what the user will see. |\n| **`name`** \\* | The name of the breakpoint. |\n| **`width`** \\* | The width of the breakpoint. This is used to set the width of the iframe. |\n| **`height`** \\* | The height of the breakpoint. This is used to set the height of the iframe. |\n\n_\\* An asterisk denotes that a property is required._\n\n{/* IMAGE OF TOOLBAR HERE */}\n\nThe \"Responsive\" option is always available in the drop-down and requires no additional configuration. This is the default breakpoint that will be used on initial load. This option styles the iframe with a width and height of `100%` so that it fills the screen at its maximum size and automatically resizes as the window changes size.\n\nYou may also explicitly resize the Live Preview by using the corresponding inputs in the toolbar. This will temporarily override the breakpoint selection to \"Custom\" until a predefined breakpoint is selected once again.\n\nIf you prefer to freely resize the Live Preview without the use of breakpoints, you can open it in a new window by clicking the button in the toolbar. This will close the iframe and open a new window which can be resized as you wish. Closing it will automatically re-open the iframe.\n\n## Example\n\nFor a working demonstration of this, check out the official [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload).\n"])</script><script>self.__next_f.push([1,"88:T1bb1,"])</script><script>self.__next_f.push([1,"\n\u003cBanner type=\"info\"\u003e\n Server-side Live Preview is only for front-end frameworks that support the concept of Server Components, i.e. [React Server Components](https://react.dev/reference/rsc/server-components). If your front-end application is built with a client-side framework like the [Next.js Pages Router](https://nextjs.org/docs/pages), [React Router](https://reactrouter.com), [Vue 3](https://vuejs.org), etc., see [client-side Live Preview](./client).\n\u003c/Banner\u003e\n\nServer-side Live Preview works by making a roundtrip to the server every time your document is saved, i.e. draft save, autosave, or publish. While using Live Preview, the Admin Panel emits a new `window.postMessage` event which your front-end application can use to invoke this process. In Next.js, this means simply calling `router.refresh()` which will hydrate the HTML using new data straight from the [Local API](../local-api/overview).\n\n\u003cBanner type=\"warning\"\u003e\n It is recommended that you enable [Autosave](../versions/autosave) alongside Live Preview to make the experience feel more responsive.\n\u003c/Banner\u003e\n\nIf your front-end application is built with [React](#react), you can use the `RefreshRouteOnChange` function that Payload provides. In the future, all other major frameworks like Vue and Svelte will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own router refresh component](#building-your-own-router-refresh-component) for more information.\n\n## React\n\nIf your front-end application is built with server-side [React](https://react.dev) like [Next.js App Router](https://nextjs.org/docs/app), you can use the `RefreshRouteOnSave` component that Payload provides.\n\nFirst, install the `@payloadcms/live-preview-react` package:\n\n```bash\nnpm install @payloadcms/live-preview-react\n```\n\nThen, render the `RefreshRouteOnSave` component anywhere in your `page.tsx`. Here's an example:\n\n`page.tsx`:\n\n```tsx\nimport { RefreshRouteOnSave } from './RefreshRouteOnSave.tsx'\nimport { getPayload } from 'payload'\nimport config from '../payload.config'\n\nexport default async function Page() {\n const payload = await getPayload({ config })\n\n const page = await payload.findByID({\n collection: 'pages',\n id: '123',\n draft: true\n })\n\n return (\n \u003cFragment\u003e\n \u003cRefreshRouteOnSave /\u003e\n \u003ch1\u003e{page.title}\u003c/h1\u003e\n \u003c/Fragment\u003e\n )\n}\n```\n\n`RefreshRouteOnSave.tsx`:\n\n```tsx\n'use client'\nimport { RefreshRouteOnSave as PayloadLivePreview } from '@payloadcms/live-preview-react'\nimport { useRouter } from 'next/navigation.js'\nimport React from 'react'\n\nexport const RefreshRouteOnSave: React.FC = () =\u003e {\n const router = useRouter()\n\n return (\n \u003cPayloadLivePreview\n refresh={() =\u003e router.refresh()}\n serverURL={process.env.NEXT_PUBLIC_PAYLOAD_URL}\n /\u003e\n )\n}\n```\n\n## Building your own router refresh component\n\nNo matter what front-end framework you are using, you can build your own component using the same underlying tooling that Payload provides.\n\nFirst, install the base `@payloadcms/live-preview` package:\n\n```bash\nnpm install @payloadcms/live-preview\n```\n\nThis package provides the following functions:\n\n| Path | Description |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |\n| **`ready`** | Sends a `window.postMessage` event to the Admin Panel to indicate that the front-end is ready to receive messages. |\n| **`isDocumentEvent`** | Checks if a `MessageEvent` originates from the Admin Panel and is a document-level event, i.e. draft save, autosave, publish, etc. |\n\nWith these functions, you can build your own hook using your front-end framework of choice:\n\n```tsx\nimport { ready, isDocumentEvent } from '@payloadcms/live-preview'\n\n// To build your own component:\n// 1. Listen for document-level `window.postMessage` events sent from the Admin Panel\n// 2. Tell the Admin Panel when it is ready to receive messages\n// 3. Refresh the route every time a new document-level event is received\n// 4. Unsubscribe from the `window.postMessage` events when it unmounts\n```\n\nHere is an example of what the same `RefreshRouteOnSave` React component from above looks like under the hood:\n\n```tsx\n'use client'\n\nimport type React from 'react'\n\nimport { isDocumentEvent, ready } from '@payloadcms/live-preview'\nimport { useCallback, useEffect, useRef } from 'react'\n\nexport const RefreshRouteOnSave: React.FC\u003c{\n apiRoute?: string\n depth?: number\n refresh: () =\u003e void\n serverURL: string\n}\u003e = (props) =\u003e {\n const { apiRoute, depth, refresh, serverURL } = props\n const hasSentReadyMessage = useRef\u003cboolean\u003e(false)\n\n const onMessage = useCallback(\n (event: MessageEvent) =\u003e {\n if (isDocumentEvent(event, serverURL)) {\n if (typeof refresh === 'function') {\n refresh()\n }\n }\n },\n [refresh, serverURL],\n )\n\n useEffect(() =\u003e {\n if (typeof window !== 'undefined') {\n window.addEventListener('message', onMessage)\n }\n\n if (!hasSentReadyMessage.current) {\n hasSentReadyMessage.current = true\n\n ready({\n serverURL,\n })\n }\n\n return () =\u003e {\n if (typeof window !== 'undefined') {\n window.removeEventListener('message', onMessage)\n }\n }\n }, [serverURL, onMessage, depth, apiRoute])\n\n return null\n}\n```\n\n## Example\n\nFor a working demonstration of this, check out the official [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload). There you will find a fully working example of how to implement Live Preview in your Next.js App Router application.\n\n## Troubleshooting\n\n#### Updates do not appear as fast as client-side Live Preview\n\nIf you are noticing that updates feel less snappy than client-side Live Preview (i.e. the `useLivePreview` hook), this is because of how the two differ in how they work—instead of emitting events against _form state_, server-side Live Preview refreshes the route after a new document is _saved_.\n\nUse [Autosave](../versions/autosave) to mimic this effect server-side. Try decreasing the value of `versions.autoSave.interval` to make the experience feel more responsive:\n\n```ts\n// collection.ts\n{\n versions: {\n drafts: {\n autosave: {\n interval: 375,\n },\n },\n },\n}\n```\n\n#### Iframe refuses to connect\n\nIf your front-end application has set a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP) that blocks the Admin Panel from loading your front-end application, the iframe will not be able to load your site. To resolve this, you can whitelist the Admin Panel's domain in your CSP by setting the `frame-ancestors` directive:\n\n```plaintext\nframe-ancestors: \"self\" localhost:* https://your-site.com;\n```\n"])</script><script>self.__next_f.push([1,"89:T3372,"])</script><script>self.__next_f.push([1,"\n\u003cBanner type=\"info\"\u003e\n If your front-end application supports Server Components like the [Next.js App Router](https://nextjs.org/docs/app), etc., we suggest setting up [server-side Live Preview](./server) instead.\n\u003c/Banner\u003e\n\nWhile using Live Preview, the [Admin Panel](../admin/overview) emits a new `window.postMessage` event every time your document has changed. Your front-end application can listen for these events and re-render accordingly.\n\nIf your front-end application is built with [React](#react) or [Vue](#vue), use the `useLivePreview` hooks that Payload provides. In the future, all other major frameworks like Svelte will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own hook](#building-your-own-hook) for more information.\n\nBy default, all hooks accept the following args:\n\n| Path | Description |\n| ------------------ | -------------------------------------------------------------------------------------- |\n| **`serverURL`** \\* | The URL of your Payload server. |\n| **`initialData`** | The initial data of the document. The live data will be merged in as changes are made. |\n| **`depth`** | The depth of the relationships to fetch. Defaults to `0`. |\n| **`apiRoute`** | The path of your API route as defined in `routes.api`. Defaults to `/api`. |\n\n_\\* An asterisk denotes that a property is required._\n\nAnd return the following values:\n\n| Path | Description |\n| --------------- | ---------------------------------------------------------------- |\n| **`data`** | The live data of the document, merged with the initial data. |\n| **`isLoading`** | A boolean that indicates whether or not the document is loading. |\n\n\u003cBanner type=\"info\"\u003e\n If your front-end is tightly coupled to required fields, you should ensure that your UI does not\n break when these fields are removed. For example, if you are rendering something like\n `data.relatedPosts[0].title`, your page will break once you remove the first related post. To get\n around this, use conditional logic, optional chaining, or default values in your UI where needed.\n For example, `data?.relatedPosts?.[0]?.title`.\n\u003c/Banner\u003e\n\n\u003cBanner type=\"info\"\u003e\n It is important that the `depth` argument matches exactly with the depth of your initial page request. The depth property is used to populated relationships and uploads beyond their IDs. See [Depth](../queries/depth) for more information.\n\u003c/Banner\u003e\n\n## Frameworks\n\nLive Preview will work with any front-end framework that supports the native [`window.postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API. By default, Payload officially supports the most popular frameworks, including:\n\n- [React](#react)\n- [Vue](#vue)\n\nIf your framework is not listed, you can still integrate with Live Preview using the underlying tooling that Payload provides. [More details](#building-your-own-hook).\n\n### React\n\nIf your front-end application is built with client-side [React](https://react.dev) like [Next.js Pages Router](https://nextjs.org/docs/pages), you can use the `useLivePreview` hook that Payload provides.\n\nFirst, install the `@payloadcms/live-preview-react` package:\n\n```bash\nnpm install @payloadcms/live-preview-react\n```\n\nThen, use the `useLivePreview` hook in your React component:\n\n```tsx\n'use client'\nimport { useLivePreview } from '@payloadcms/live-preview-react'\nimport { Page as PageType } from '@/payload-types'\n\n// Fetch the page in a server component, pass it to the client component, then thread it through the hook\n// The hook will take over from there and keep the preview in sync with the changes you make\n// The `data` property will contain the live data of the document\nexport const PageClient: React.FC\u003c{\n page: {\n title: string\n }\n}\u003e = ({ page: initialPage }) =\u003e {\n const { data } = useLivePreview\u003cPageType\u003e({\n initialData: initialPage,\n serverURL: PAYLOAD_SERVER_URL,\n depth: 2,\n })\n\n return \u003ch1\u003e{data.title}\u003c/h1\u003e\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n If you are using [React Server Components](https://react.dev/reference/rsc/server-components), we strongly suggest setting up [server-side Live Preview](./server) instead.\n\u003c/Banner\u003e\n\n### Vue\n\nIf your front-end application is built with [Vue 3](https://vuejs.org) or [Nuxt 3](https://nuxt.js), you can use the `useLivePreview` composable that Payload provides.\n\nFirst, install the `@payloadcms/live-preview-vue` package:\n\n```bash\nnpm install @payloadcms/live-preview-vue\n```\n\nThen, use the `useLivePreview` hook in your Vue component:\n\n```ts\n\u003cscript setup lang=\"ts\"\u003e\nimport type { PageData } from '~/types';\nimport { defineProps } from 'vue';\nimport { useLivePreview } from '@payloadcms/live-preview-vue';\n\n// Fetch the initial data on the parent component or using async state\nconst props = defineProps\u003c{ initialData: PageData }\u003e();\n\n// The hook will take over from here and keep the preview in sync with the changes you make.\n// The `data` property will contain the live data of the document only when viewed from the Preview view of the Admin UI.\nconst { data } = useLivePreview\u003cPageData\u003e({\n initialData: props.initialData,\n serverURL: \"\u003cPAYLOAD_SERVER_URL\u003e\",\n depth: 2,\n});\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n \u003ch1\u003e{{ data.title }}\u003c/h1\u003e\n\u003c/template\u003e\n```\n\n## Building your own hook\n\nNo matter what front-end framework you are using, you can build your own hook using the same underlying tooling that Payload provides.\n\nFirst, install the base `@payloadcms/live-preview` package:\n\n```bash\nnpm install @payloadcms/live-preview\n```\n\nThis package provides the following functions:\n\n| Path | Description |\n| ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |\n| **`subscribe`** | Subscribes to the Admin Panel's `window.postMessage` events and calls the provided callback function. |\n| **`unsubscribe`** | Unsubscribes from the Admin Panel's `window.postMessage` events. |\n| **`ready`** | Sends a `window.postMessage` event to the Admin Panel to indicate that the front-end is ready to receive messages. |\n| **`isLivePreviewEvent`** | Checks if a `MessageEvent` originates from the Admin Panel and is a Live Preview event, i.e. debounced form state. |\n\nThe `subscribe` function takes the following args:\n\n| Path | Description |\n| ------------------ | ------------------------------------------------------------------------------------------- |\n| **`callback`** \\* | A callback function that is called with `data` every time a change is made to the document. |\n| **`serverURL`** \\* | The URL of your Payload server. |\n| **`initialData`** | The initial data of the document. The live data will be merged in as changes are made. |\n| **`depth`** | The depth of the relationships to fetch. Defaults to `0`. |\n\nWith these functions, you can build your own hook using your front-end framework of choice:\n\n```tsx\nimport { subscribe, unsubscribe } from '@payloadcms/live-preview'\n\n// To build your own hook, subscribe to Live Preview events using the `subscribe` function\n// It handles everything from:\n// 1. Listening to `window.postMessage` events\n// 2. Merging initial data with active form state\n// 3. Populating relationships and uploads\n// 4. Calling the `onChange` callback with the result\n// Your hook should also:\n// 1. Tell the Admin Panel when it is ready to receive messages\n// 2. Handle the results of the `onChange` callback to update the UI\n// 3. Unsubscribe from the `window.postMessage` events when it unmounts\n```\n\nHere is an example of what the same `useLivePreview` React hook from above looks like under the hood:\n\n```tsx\nimport { subscribe, unsubscribe, ready } from '@payloadcms/live-preview'\nimport { useCallback, useEffect, useState, useRef } from 'react'\n\nexport const useLivePreview = \u003cT extends any\u003e(props: {\n depth?: number\n initialData: T\n serverURL: string\n}): {\n data: T\n isLoading: boolean\n} =\u003e {\n const { depth = 0, initialData, serverURL } = props\n const [data, setData] = useState\u003cT\u003e(initialData)\n const [isLoading, setIsLoading] = useState\u003cboolean\u003e(true)\n const hasSentReadyMessage = useRef\u003cboolean\u003e(false)\n\n const onChange = useCallback((mergedData) =\u003e {\n // When a change is made, the `onChange` callback will be called with the merged data\n // Set this merged data into state so that React will re-render the UI\n setData(mergedData)\n setIsLoading(false)\n }, [])\n\n useEffect(() =\u003e {\n // Listen for `window.postMessage` events from the Admin Panel\n // When a change is made, the `onChange` callback will be called with the merged data\n const subscription = subscribe({\n callback: onChange,\n depth,\n initialData,\n serverURL,\n })\n\n // Once subscribed, send a `ready` message back up to the Admin Panel\n // This will indicate that the front-end is ready to receive messages\n if (!hasSentReadyMessage.current) {\n hasSentReadyMessage.current = true\n\n ready({\n serverURL,\n })\n }\n\n // When the component unmounts, unsubscribe from the `window.postMessage` events\n return () =\u003e {\n unsubscribe(subscription)\n }\n }, [serverURL, onChange, depth, initialData])\n\n return {\n data,\n isLoading,\n }\n}\n```\n\n\u003cBanner type=\"info\"\u003e\n When building your own hook, ensure that the args and return values are consistent with the ones\n listed at the top of this document. This will ensure that all hooks follow the same API.\n\u003c/Banner\u003e\n\n## Example\n\nFor a working demonstration of this, check out the official [Live Preview Example](https://github.com/payloadcms/payload/tree/main/examples/live-preview/payload). There you will find examples of various front-end frameworks and how to integrate each one of them, including:\n\n- [Next.js App Router](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-app)\n- [Next.js Pages Router](https://github.com/payloadcms/payload/tree/main/examples/live-preview/next-pages)\n\n## Troubleshooting\n\n#### Relationships and/or uploads are not populating\n\nIf you are using relationships or uploads in your front-end application, and your front-end application runs on a different domain than your Payload server, you may need to configure [CORS](../configuration/overview) to allow requests to be made between the two domains. This includes sites that are running on a different port or subdomain. Similarly, if you are protecting resources behind user authentication, you may also need to configure [CSRF](../authentication/overview#csrf-protection) to allow cookies to be sent between the two domains. For example:\n\n```ts\n// payload.config.ts\n{\n // ...\n // If your site is running on a different domain than your Payload server,\n // This will allows requests to be made between the two domains\n cors: {\n [\n 'http://localhost:3001' // Your front-end application\n ],\n },\n // If you are protecting resources behind user authentication,\n // This will allow cookies to be sent between the two domains\n csrf: {\n [\n 'http://localhost:3001' // Your front-end application\n ],\n },\n}\n```\n\n#### Relationships and/or uploads disappear after editing a document\n\nIt is possible that either you are setting an improper [`depth`](../queries/depth) in your initial request and/or your `useLivePreview` hook, or they're mismatched. Ensure that the `depth` parameter is set to the correct value, and that it matches exactly in both places. For example:\n\n```tsx\n// Your initial request\nconst { docs } = await payload.find({\n collection: 'pages',\n depth: 1, // Ensure this is set to the proper depth for your application\n where: {\n slug: {\n equals: 'home',\n },\n },\n})\n```\n\n```tsx\n// Your hook\nconst { data } = useLivePreview\u003cPageType\u003e({\n initialData: initialPage,\n serverURL: PAYLOAD_SERVER_URL,\n depth: 1, // Ensure this matches the depth of your initial request\n})\n```\n\n#### Iframe refuses to connect\n\nIf your front-end application has set a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP) that blocks the Admin Panel from loading your front-end application, the iframe will not be able to load your site. To resolve this, you can whitelist the Admin Panel's domain in your CSP by setting the `frame-ancestors` directive:\n\n```plaintext\nframe-ancestors: \"self\" localhost:* https://your-site.com;\n```\n"])</script><script>self.__next_f.push([1,"8a:T2e6c,"])</script><script>self.__next_f.push([1,"\n\u003cBanner\u003e\n Payload's powerful Versions functionality allows you to keep a running history of changes over\n time and extensible to fit any content publishing workflow.\n\u003c/Banner\u003e\n\nWhen enabled, Payload will automatically scaffold a new Collection in your database to store versions of your document(s) over time, and the Admin UI will be extended with additional views that allow you to browse document versions, view diffs in order to see exactly what has changed in your documents (and when they changed), and restore documents back to prior versions easily.\n\n![Versions](/images/docs/versions.png)\n_Comparing an old version to a newer version of a document_\n\n**With Versions, you can:**\n\n- Maintain an audit log / history of every change ever made to a document, including monitoring for what user made which change\n- Restore documents and globals to prior states in case you need to roll back changes\n- Build a true [Draft Preview](../versions/drafts) mode for your data\n- Manage who can see Drafts, and who can only see Published documents via [Access Control](../access-control/overview)\n- Enable [Autosave](../versions/autosave) on collections and globals to never lose your work again\n- Build a powerful publishing schedule mechanism to create documents and have them become publicly readable automatically at a future date\n\n\u003cBanner type=\"success\"\u003e\n Versions are extremely performant and totally opt-in. They don't change the shape of your data at\n all. All versions are stored in a separate Collection and can be turned on and off easily at your\n discretion.\n\u003c/Banner\u003e\n\n## Options\n\nVersions support a few different levels of functionality that each come with their own impacts to document workflow.\n\n### Versions enabled, drafts disabled\n\nIf you enable versions but keep draft mode disabled, Payload will simply create a new version of a document each time you update a document. This is great for use cases where you need to retain a history of all document updates over time, but always want to treat the newest document version as the version that is \"published\".\n\nFor example, a use case for \"versions enabled, drafts disabled\" could be on a collection of users, where you might want to keep a version history (or audit log) of all changes ever made to users - but any changes to users should _always_ be treated as \"published\" and you have no need to maintain a \"draft\" version of a user.\n\n### Versions and drafts enabled\n\nIf you have versions _and_ drafts enabled, you are able to control which documents are published, and which are considered draft. That lets you write [Access Control](../access-control/overview) to control who can see published documents, and who can see draft documents. It also lets you save versions (drafts) that are _newer_ than your most recently published document, which is helpful if you want to draft changes and maybe even preview them before you publish the changes. Read more about Drafts [here](../versions/drafts).\n\n### Versions, drafts, and autosave enabled\n\nWhen you have versions, drafts, _and_ `autosave` enabled, the Admin UI will automatically save changes that you make to a new `draft` version as you edit a document, which makes sure that you never lose your changes ever again. Autosave will not affect your published post at all—instead, it'll just save your changes and let you publish them whenever you or your editors are ready to do so. Read more about Autosave [here](../versions/autosave).\n\n## Collection config\n\nConfiguring Versions is done by adding the `versions` key to your Collection configs. Set it to `true` to enable default Versions settings, or customize versions options by setting the property equal to an object containing the following available options:\n\n| Option | Description |\n| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `maxPerDoc` | Use this setting to control how many versions to keep on a document by document basis. Must be an integer. Defaults to 100, use 0 to save all versions. |\n| `drafts ` | Enable [Drafts](../versions/drafts) mode for this collection. To enable, set to `true` or pass an object with `draft` [options](../versions/drafts#options). |\n\n## Global config\n\nGlobal versions work similarly to Collection versions but have a slightly different set of config properties supported.\n\n| Option | Description |\n| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `max` | Use this setting to control how many versions to keep on a global by global basis. Must be an integer. |\n| `drafts` | Enable [Drafts](../versions/drafts) mode for this global. To enable, set to `true` or pass an object with `draft` [options](../versions/drafts#options) |\n\n### Database impact\n\nBy enabling `versions`, a new database collection will be made to store versions for your collection or global. The collection will be named based off the `slug` of the collection or global and will follow this pattern (where `slug` is replaced with the `slug` of your collection or global):\n\n```\n_slug_versions\n```\n\nEach document in this new `versions` collection will store a set of meta properties about the version as well as a _full_ copy of the document. For example, a version's data might look like this for a Collection document:\n\n```json\n{\n \"_id\": \"61cf752c19cdf1b1af7b61f1\", // a unique ID of this version\n \"parent\": \"61ce1354091d5b3ffc20ea6e\", // the ID of the parent document\n \"autosave\": false, // used to denote if this version was created via autosave\n \"version\": {\n // your document's data goes here\n // all fields are set to not required and this property can be partially complete\n },\n \"createdAt\": \"2021-12-31T21:25:00.992+00:00\",\n \"updatedAt\": \"2021-12-31T21:25:00.992+00:00\"\n}\n```\n\nGlobal versions are stored the same as the collection version shown above, except they do not feature the `parent` property, as each Global receives its own `versions` collection. That means we know that all versions in that collection correspond to that specific global.\n\n## Version operations\n\nVersions expose new operations for both collections and globals. They allow you to find and query versions, find a single version by ID, and publish (or restore) a version by ID. Both Collections and Globals support the same new operations. They are used primarily by the admin UI, but if you are writing custom logic in your app and would like to utilize them, they're available for you to use as well via REST, GraphQL, and Local APIs.\n\n**Collection REST endpoints:**\n\n| Method | Path | Description |\n| ------ | ------------------------------------ | --------------------------------- |\n| `GET` | `/api/{collectionSlug}/versions` | Find and query paginated versions |\n| `GET` | `/api/{collectionSlug}/versions/:id` | Find a specific version by ID |\n| `POST` | `/api/{collectionSlug}/versions/:id` | Restore a version by ID |\n\n**Collection GraphQL queries:**\n\n| Query Name | Operation |\n| ---------------------------------------- | ----------------- |\n| **`version{collection.label.singular}`** | `findVersionByID` |\n| **`versions{collection.label.plural}`** | `findVersions` |\n\n**And mutation:**\n\n| Query Name | Operation |\n| ----------------------------------------------- | ---------------- |\n| **`restoreVersion{collection.label.singular}`** | `restoreVersion` |\n\n**Collection Local API methods:**\n\n### Find\n\n```js\n// Result will be a paginated set of Versions.\n// See /docs/queries/pagination for more.\nconst result = await payload.findVersions({\n collection: 'posts', // required\n depth: 2,\n page: 1,\n limit: 10,\n where: {}, // pass a `where` query here\n sort: '-createdAt',\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n### Find by ID\n\n```js\n// Result will be a Post document.\nconst result = await payload.findVersionByID({\n collection: 'posts', // required\n id: '507f1f77bcf86cd799439013', // required\n depth: 2,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n### Restore\n\n```js\n// Result will be the restored global document.\nconst result = await payload.restoreVersion({\n collection: 'posts', // required\n id: '507f1f77bcf86cd799439013', // required\n depth: 2,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n**Global REST endpoints:**\n\n| Method | Path | Description |\n| ------ | ---------------------------------------- | --------------------------------- |\n| `GET` | `/api/globals/{globalSlug}/versions` | Find and query paginated versions |\n| `GET` | `/api/globals/{globalSlug}/versions/:id` | Find a specific version by ID |\n| `POST` | `/api/globals/{globalSlug}/versions/:id` | Restore a version by ID |\n\n**Global GraphQL queries:**\n\n| Query Name | Operation |\n| ---------------------------- | ----------------- |\n| **`version{global.label}`** | `findVersionByID` |\n| **`versions{global.label}`** | `findVersions` |\n\n**Global GraphQL mutation:**\n\n| Query Name | Operation |\n| ---------------------------------- | ---------------- |\n| **`restoreVersion{global.label}`** | `restoreVersion` |\n\n**Global Local API methods:**\n\n### Find\n\n```js\n// Result will be a paginated set of Versions.\n// See /docs/queries/pagination for more.\nconst result = await payload.findGlobalVersions({\n slug: 'header', // required\n depth: 2,\n page: 1,\n limit: 10,\n where: {}, // pass a `where` query here\n sort: '-createdAt',\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n### Find by ID\n\n```js\n// Result will be a Post document.\nconst result = await payload.findGlobalVersionByID({\n slug: 'header', // required\n id: '507f1f77bcf86cd799439013', // required\n depth: 2,\n locale: 'en',\n fallbackLocale: false,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n### Restore\n\n```js\n// Result will be the restored global document.\nconst result = await payload.restoreGlobalVersion({\n slug: 'header', // required\n id: '507f1f77bcf86cd799439013', // required\n depth: 2,\n user: dummyUser,\n overrideAccess: false,\n showHiddenFields: true,\n})\n```\n\n## Access Control\n\nVersions expose a new [Access Control](../access-control/overview) function on both [Collections](../configuration/collections) and [Globals](../configuration/globals) that allow for you to control who can see versions of documents, and who can't.\n\n| Function | Allows/Denies Access |\n| ------------------ | ---------------------------------------------------------------------------------------------------------------------- |\n| **`readVersions`** | Used to control who can read versions, and who can't. Will automatically restrict the Admin UI version viewing access. |\n\nFor full details on how to use Access Control with Versions, see the [Access Control](../access-control/overview) documentation.\n"])</script><script>self.__next_f.push([1,"8b:T22b2,"])</script><script>self.__next_f.push([1,"\nPayload's Draft functionality builds on top of the Versions functionality to allow you to make changes to your collection documents and globals, but publish only when you're ready. This functionality allows you to build powerful Preview environments for your data, where you can make sure your changes look good before publishing documents.\n\n\u003cBanner type=\"warning\"\u003eDrafts rely on Versions being enabled in order to function.\u003c/Banner\u003e\n\nBy enabling Versions with Drafts, your collections and globals can maintain _newer_, and _unpublished_ versions of your documents. It's perfect for cases where you might want to work on a document, update it and save your progress, but not necessarily make it publicly published right away. Drafts are extremely helpful when building preview implementations.\n\n![Drafts Enabled](/images/docs/drafts-enabled.png)\n_If Drafts are enabled, the typical Save button is replaced with new actions which allow you to either save a draft, or publish your changes._\n\n## Options\n\nCollections and Globals both support the same options for configuring drafts. You can either set `versions.drafts` to `true`, or pass an object to configure draft properties.\n\n| Draft Option | Description |\n| ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `autosave` | Enable `autosave` to automatically save progress while documents are edited. To enable, set to `true` or pass an object with [options](../versions/autosave). |\n| `validate` | Set `validate` to `true` to validate draft documents when saved. Default is `false`. |\n\n## Database changes\n\nBy enabling drafts on a collection or a global, Payload will \u003cstrong\u003eautomatically inject a new field into your schema\u003c/strong\u003e called `_status`. The `_status` field is used internally by Payload to store if a document is set to `draft` or `published`.\n\n**Admin UI status indication**\n\nWithin the Admin UI, if drafts are enabled, a document can be shown with one of three \"statuses\":\n\n1. \u003cstrong\u003eDraft\u003c/strong\u003e - if a document has never been published, and only draft versions of the document\n are present\n1. \u003cstrong\u003ePublished\u003c/strong\u003e - if a document is published and there are no newer drafts available\n1. \u003cstrong\u003eChanged\u003c/strong\u003e - if a document has been published, but there are newer drafts available\n and not yet published\n\n## Draft API\n\n\u003cBanner type=\"success\"\u003e\n If drafts are enabled on your collection or global, important and powerful changes are made to\n your REST, GraphQL, and Local APIs that allow you to specify if you are interacting with drafts or\n with live documents.\n\u003c/Banner\u003e\n\n#### Updating or creating drafts\n\nIf you enable drafts on a collection or global, the `create` and `update` operations for REST, GraphQL, and Local APIs expose a new option called `draft` which allows you to specify if you are creating or updating a \u003cstrong\u003edraft\u003c/strong\u003e, or if you're just sending your changes straight to the published document. For example, if you pass the query parameter `?draft=true` to a REST `create` or `update` operation, your action will be treated as if you are creating a `draft` and not a published document. By default, the `draft` argument is set to `false`.\n\n**Required fields**\n\nIf `draft` is enabled while creating or updating a document, all fields are considered as not required, so that you can save drafts that are incomplete.\n\n#### Reading drafts vs. published documents\n\nIn addition to the `draft` argument within `create` and `update` operations, a `draft` argument is also exposed for `find` and `findByID` operations.\n\nIf `draft` is set to `true` while reading a document, \u003cstrong\u003ePayload will automatically replace returned document(s) with their newest drafts\u003c/strong\u003e if any newer drafts are available.\n\n\u003cstrong\u003eFor example, let's take the following scenario:\u003c/strong\u003e\n\n1. You create a new collection document and publish it right away\n1. You then make some updates, and save the updates as a draft\n1. You then make some further updates, and save more updates as another draft\n\nHere, you will have a published document that resides in your main collection, and then you'll have two _newer_ drafts that reside in the `_[collectionSlug]_versions` database collection.\n\nIf you simply fetch your created document using a `find` or `findByID` operation, your published document will be returned and the drafts will be ignored.\n\nBut, if you specify `draft` as `true`, Payload will automatically replace your published document's content with content coming from the most recently saved `version`. In this case, as we have created _two_ versions in the above scenario, Payload will send back data from the newest (second) draft and your document will appear as the most recently drafted version instead of the published version.\n\n## Controlling who can see Collection drafts\n\n\u003cBanner type=\"warning\"\u003e\n If you're using the \u003cstrong\u003edrafts\u003c/strong\u003e feature, it's important for you to consider who can\n view your drafts, and who can view only published documents. Luckily, Payload makes this extremely\n simple and puts the power completely in your hands.\n\u003c/Banner\u003e\n\n#### Restricting draft access\n\nYou can use the `read` [Access Control](../access-control/collections#read) method to restrict who is able to view drafts of your documents by simply returning a [query constraint](../queries/overview) which restricts the documents that any given user is able to retrieve.\n\nHere is an example that utilizes the `_status` field to require a user to be logged in to retrieve drafts:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Pages: CollectionConfig = {\n slug: 'pages',\n access: {\n read: ({ req }) =\u003e {\n // If there is a user logged in,\n // let them retrieve all documents\n if (req.user) return true\n\n // If there is no user,\n // restrict the documents that are returned\n // to only those where `_status` is equal to `published`\n return {\n _status: {\n equals: 'published',\n },\n }\n },\n },\n versions: {\n drafts: true,\n },\n //.. the rest of the Pages config here\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote regarding adding versions to an existing collection\u003c/strong\u003e\n \u003cbr /\u003e\n If you already have a collection with documents, and you \u003cem\u003eopt in\u003c/em\u003e to draft functionality\n after you have already created existing documents, all of your old documents{' '}\n \u003cem\u003ewill not have a _status field\u003c/em\u003e until you resave them. For this reason, if you are{' '}\n \u003cem\u003eadding\u003c/em\u003e versions into an existing collection, you might want to write your Access Control\n function to allow for users to read both documents where{' '}\n \u003cstrong\u003e_status is equal to \"published\"\u003c/strong\u003e as well as where{' '}\n \u003cstrong\u003e_status does not exist\u003c/strong\u003e.\n\u003c/Banner\u003e\n\nHere is an example for how to write an [Access Control](../access-control/overview) function that grants access to both documents where `_status` is equal to \"published\" and where `_status` does not exist:\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Pages: CollectionConfig = {\n slug: 'pages',\n access: {\n read: ({ req }) =\u003e {\n // If there is a user logged in,\n // let them retrieve all documents\n if (req.user) return true\n\n // If there is no user,\n // restrict the documents that are returned\n // to only those where `_status` is equal to `published`\n // or where `_status` does not exist\n return {\n or: [\n {\n _status: {\n equals: 'published',\n },\n },\n {\n _status: {\n exists: false,\n },\n },\n ],\n }\n },\n },\n versions: {\n drafts: true,\n },\n //.. the rest of the Pages config here\n}\n```\n\n## Unpublishing drafts\n\nIf a document is published, the Payload Admin UI will be updated to show an \"unpublish\" button at the top of the sidebar, which will \"unpublish\" the currently published document. Consider this as a way to \"revert\" a document back to a draft state. On the API side, this is done by simply setting `_status: 'draft'` on any document.\n\n## Reverting to published\n\nIf a document is published, and you have made further changes which are saved as a draft, Payload will show a \"revert to published\" button at the top of the sidebar which will allow you to reject your draft changes and \"revert\" back to the published state of the document. Your drafts will still be saved, but a new version will be created that will reflect the last published state of the document.\n"])</script><script>self.__next_f.push([1,"8c:Td4c,"])</script><script>self.__next_f.push([1,"\nExtending on Payload's [Draft](../versions/drafts) functionality, you can configure your collections and globals to autosave changes as drafts, and publish only you're ready. The Admin UI will automatically adapt to autosaving progress at an interval that you define, and will store all autosaved changes as a new Draft version. Never lose your work - and publish changes to the live document only when you're ready.\n\n\u003cBanner type=\"warning\"\u003e\n Autosave relies on Versions and Drafts being enabled in order to function.\n\u003c/Banner\u003e\n\n![Autosave Enabled](/images/docs/autosave-enabled.png)\n_If Autosave is enabled, drafts will be created automatically as the document is modified and the Admin UI adds an indicator describing when the document was last saved to the top right of the sidebar._\n\n## Options\n\nCollections and Globals both support the same options for configuring autosave. You can either set `versions.drafts.autosave` to `true`, or pass an object to configure autosave properties.\n\n| Drafts Autosave Options | Description |\n| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `interval` | Define an `interval` in milliseconds to automatically save progress while documents are edited. Document updates are \"debounced\" at this interval. Defaults to `800`. |\n\n**Example config with versions, drafts, and autosave enabled:**\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Pages: CollectionConfig = {\n slug: 'pages',\n access: {\n read: ({ req }) =\u003e {\n // If there is a user logged in,\n // let them retrieve all documents\n if (req.user) return true\n\n // If there is no user,\n // restrict the documents that are returned\n // to only those where `_status` is equal to `published`\n return {\n _status: {\n equals: 'published',\n },\n }\n },\n },\n versions: {\n drafts: {\n autosave: true,\n\n // Alternatively, you can specify an `interval`:\n // autosave: {\n // interval: 1500,\n // },\n },\n },\n //.. the rest of the Pages config here\n}\n```\n\n## Autosave API\n\nWhen `autosave` is enabled, all `update` operations within Payload expose a new argument called `autosave`. When set to `true`, Payload will treat the incoming draft update as an `autosave`. This is primarily used by the Admin UI, but there may be some cases where you are building an app for your users and wish to implement `autosave` in your own app. To do so, use the `autosave` argument in your `update` operations.\n\n### How autosaves are stored\n\nIf we created a new version for each autosave, you'd quickly find a ton of autosaves that clutter up your `_versions` collection within the database. That would be messy quick because `autosave` is typically set to save a document at ~800ms intervals.\n\n\u003cBanner type=\"success\"\u003e\n Instead of creating a new version each time a document is autosaved, Payload smartly only creates{' '}\n \u003cstrong\u003eone\u003c/strong\u003e autosave version, and then updates that specific version with each autosave\n performed. This makes sure that your versions remain nice and tidy.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"8d:T3ccf,"])</script><script>self.__next_f.push([1,"\n\u003cBanner\u003e\n Payload provides everything you need to enable file upload, storage, and management directly\n on your server—including extremely powerful file [access control](#access-control).\n\u003c/Banner\u003e\n\n\u003cLightDarkImage\n srcLight=\"https://payloadcms.com/images/docs/upload-admin.jpg\"\n srcDark=\"https://payloadcms.com/images/docs/upload-admin.jpg\"\n alt=\"Shows an Upload enabled collection in the Payload Admin Panel\"\n caption=\"Admin Panel screenshot depicting a Media Collection with Upload enabled\"\n/\u003e\n\n**Here are some common use cases of Uploads:**\n\n- Creating a \"Media Library\" that contains images for use throughout your site or app\n- Building a Gated Content library where users need to sign up to gain access to downloadable assets like ebook PDFs, whitepapers, etc.\n- Storing publicly available, downloadable assets like software, ZIP files, MP4s, etc.\n\n**By simply enabling Upload functionality on a Collection, Payload will automatically transform your Collection into a robust file management / storage solution. The following modifications will be made:**\n\n1. `filename`, `mimeType`, and `filesize` fields will be automatically added to your Collection. Optionally, if you pass `imageSizes` to your Collection's Upload config, a [`sizes`](#image-sizes) array will also be added containing auto-resized image sizes and filenames.\n1. The Admin Panel will modify its built-in `List` component to show a thumbnail for each upload within the List View\n1. The Admin Panel will modify its `Edit` view(s) to add a new set of corresponding Upload UI which will allow for file upload\n1. The `create`, `update`, and `delete` Collection operations will be modified to support file upload, re-upload, and deletion\n\n## Enabling Uploads\n\nEvery Payload Collection can opt-in to supporting Uploads by specifying the `upload` property on the Collection's config to either `true` or to an object containing `upload` options.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eTip:\u003c/strong\u003e\n \u003cbr /\u003eA common pattern is to create a \u003cstrong\u003e\"media\"\u003c/strong\u003e collection and enable \u003cstrong\u003e\n upload\n \u003c/strong\u003e on that collection.\n\u003c/Banner\u003e\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Media: CollectionConfig = {\n slug: 'media',\n upload: {\n staticDir: 'media',\n imageSizes: [\n {\n name: 'thumbnail',\n width: 400,\n height: 300,\n position: 'centre',\n },\n {\n name: 'card',\n width: 768,\n height: 1024,\n position: 'centre',\n },\n {\n name: 'tablet',\n width: 1024,\n // By specifying `undefined` or leaving a height undefined,\n // the image will be sized to a certain width,\n // but it will retain its original aspect ratio\n // and calculate a height automatically.\n height: undefined,\n position: 'centre',\n },\n ],\n adminThumbnail: 'thumbnail',\n mimeTypes: ['image/*'],\n },\n fields: [\n {\n name: 'alt',\n type: 'text',\n },\n ],\n}\n```\n\n### Collection Upload Options\n\n_An asterisk denotes that an option is required._\n\n| Option | Description |\n| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`adminThumbnail`** | Set the way that the [Admin Panel](../admin/overview) will display thumbnails for this Collection. [More](#admin-thumbnails) |\n| **`bulkUpload`** | Allow users to upload in bulk from the list view, default is true |\n| **`crop`** | Set to `false` to disable the cropping tool in the [Admin Panel](../admin/overview). Crop is enabled by default. [More](#crop-and-focal-point-selector) |\n| **`disableLocalStorage`** | Completely disable uploading files to disk locally. [More](#disabling-local-upload-storage) |\n| **`displayPreview`** | Enable displaying preview of the uploaded file in Upload fields related to this Collection. Can be locally overridden by `displayPreview` option in Upload field. [More](../fields/upload#config-options). |\n| **`externalFileHeaderFilter`** | Accepts existing headers and returns the headers after filtering or modifying. |\n| **`filesRequiredOnCreate`** | Mandate file data on creation, default is true. |\n| **`filenameCompoundIndex`** | Field slugs to use for a compound index instead of the default filename index.\n| **`focalPoint`** | Set to `false` to disable the focal point selection tool in the [Admin Panel](../admin/overview). The focal point selector is only available when `imageSizes` or `resizeOptions` are defined. [More](#crop-and-focal-point-selector) |\n| **`formatOptions`** | An object with `format` and `options` that are used with the Sharp image library to format the upload file. [More](https://sharp.pixelplumbing.com/api-output#toformat) |\n| **`handlers`** | Array of Request handlers to execute when fetching a file, if a handler returns a Response it will be sent to the client. Otherwise Payload will retrieve and send back the file. |\n| **`imageSizes`** | If specified, image uploads will be automatically resized in accordance to these image sizes. [More](#image-sizes) |\n| **`mimeTypes`** | Restrict mimeTypes in the file picker. Array of valid mimetypes or mimetype wildcards [More](#mimetypes) |\n| **`resizeOptions`** | An object passed to the the Sharp image library to resize the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize) |\n| **`staticDir`** | The folder directory to use to store media in. Can be either an absolute path or relative to the directory that contains your config. Defaults to your collection slug |\n| **`trimOptions`** | An object passed to the the Sharp image library to trim the uploaded file. [More](https://sharp.pixelplumbing.com/api-resize#trim) |\n| **`withMetadata`** | If specified, appends metadata to the output image file. Accepts a boolean or a function that receives `metadata` and `req`, returning a boolean. |\n\n\n### Payload-wide Upload Options\n\nUpload options are specifiable on a Collection by Collection basis, you can also control app wide options by passing your base Payload Config an `upload` property containing an object supportive of all `Busboy` configuration options. [Click here](https://github.com/mscdex/busboy#api) for more documentation about what you can control.\n\nA common example of what you might want to customize within Payload-wide Upload options would be to increase the allowed `fileSize` of uploads sent to Payload:\n\n```ts\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n collections: [\n {\n slug: 'media',\n fields: [\n {\n name: 'alt',\n type: 'text',\n },\n ],\n upload: true,\n },\n ],\n upload: {\n limits: {\n fileSize: 5000000, // 5MB, written in bytes\n },\n },\n})\n```\n\n### Custom filename via hooks\n\nYou can customize the filename before it's uploaded to the server by using a `beforeOperation` hook.\n\n```ts\nbeforeOperation: [\n ({ req, operation }) =\u003e {\n if ((operation === 'create' || operation === 'update') \u0026\u0026 req.file) {\n req.file.name = 'test.jpg'\n }\n },\n],\n```\n\nThe `req.file` object will have additional information about the file, such as mimeType and extension, and you also have full access to the file data itself.\nThe filename from here will also be threaded to image sizes if they're enabled.\n\n## Image Sizes\n\nIf you specify an array of `imageSizes` to your `upload` config, Payload will automatically crop and resize your uploads to fit each of the sizes specified by your config.\n\nThe [Admin Panel](../admin/overview) will also automatically display all available files, including width, height, and file size, for each of your uploaded files.\n\nBehind the scenes, Payload relies on [`sharp`](https://sharp.pixelplumbing.com/api-resize#resize) to perform its image resizing. You can specify additional options for `sharp` to use while resizing your images.\n\n#### Accessing the resized images in hooks\n\nAll auto-resized images are exposed to be re-used in hooks and similar via an object that is bound to `req.payloadUploadSizes`.\n\nThe object will have keys for each size generated, and each key will be set equal to a buffer containing the file data.\n\n#### Handling Image Enlargement\n\nWhen an uploaded image is smaller than the defined image size, we have 3 options:\n\n`withoutEnlargement: undefined | false | true`\n\n1. `undefined` [default]: uploading images with smaller width AND height than the image size will return null\n2. `false`: always enlarge images to the image size\n3. `true`: if the image is smaller than the image size, return the original image\n\n\u003cBanner type=\"error\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n By default, the image size will return NULL when the uploaded image is smaller than the defined\n image size. Use the `withoutEnlargement` prop to change this.\n\u003c/Banner\u003e\n\n#### Custom file name per size\n\nEach image size supports a `generateImageName` function that can be used to generate a custom file name for the resized image.\nThis function receives the original file name, the resize name, the extension, height and width as arguments.\n\n```ts\n{\n name: 'thumbnail',\n width: 400,\n height: 300,\n generateImageName: ({ height, sizeName, extension, width }) =\u003e {\n return `custom-${sizeName}-${height}-${width}.${extension}`\n },\n}\n```\n\n## Crop and Focal Point Selector\n\nThis feature is only available for image file types.\n\nSetting `crop: false` and `focalPoint: false` in your Upload config will be disable the respective selector in the [Admin Panel](../admin/overview).\n\nImage cropping occurs before any resizing, the resized images will therefore be generated from the cropped image (**not** the original image).\n\nIf no resizing options are specified (`imageSizes` or `resizeOptions`), the focal point selector will not be displayed.\n\n## Disabling Local Upload Storage\n\nIf you are using a plugin to send your files off to a third-party file storage host or CDN, like Amazon S3 or similar, you may not want to store your files locally at all. You can prevent Payload from writing files to disk by specifying `disableLocalStorage: true` on your collection's upload config.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n This is a fairly advanced feature. If you do disable local file storage, by default, your admin\n panel's thumbnails will be broken as you will not have stored a file. It will be totally up to you\n to use either a plugin or your own hooks to store your files in a permanent manner, as well as\n provide your own admin thumbnail using \u003cstrong\u003eupload.adminThumbnail\u003c/strong\u003e.\n\u003c/Banner\u003e\n\n## Admin Thumbnails\n\nYou can specify how Payload retrieves admin thumbnails for your upload-enabled Collections with one of the following:\n\n1. `adminThumbnail` as a **string**, equal to one of your provided image size names.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Media: CollectionConfig = {\n slug: 'media',\n upload: {\n // highlight-start\n adminThumbnail: 'small',\n // highlight-end\n imageSizes: [\n {\n name: 'small',\n fit: 'cover',\n height: 300,\n width: 900,\n },\n {\n name: 'large',\n fit: 'cover',\n height: 600,\n width: 1800,\n }\n ]\n }\n}\n```\n\n2. `adminThumbnail` as a **function** that takes the document's data and sends back a full URL to load the thumbnail.\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Media: CollectionConfig = {\n slug: 'media',\n upload: {\n // highlight-start\n adminThumbnail: ({ doc }) =\u003e `https://google.com/custom-path-to-file/${doc.filename}`,\n // highlight-end\n }\n}\n```\n\n## MimeTypes\n\nSpecifying the `mimeTypes` property can restrict what files are allowed from the user's file picker. This accepts an array of strings, which can be any valid mimetype or mimetype wildcards\n\nSome example values are: `image/*`, `audio/*`, `video/*`, `image/png`, `application/pdf`\n\n**Example mimeTypes usage:**\n\n```ts\nimport type { CollectionConfig } from 'payload'\n\nexport const Media: CollectionConfig = {\n slug: 'media',\n upload: {\n mimeTypes: ['image/*', 'application/pdf'], // highlight-line\n },\n}\n```\n\n## Uploading Files\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant:\u003c/strong\u003e\n \u003cbr /\u003e\n Uploading files is currently only possible through the REST and Local APIs due to how GraphQL\n works. It's difficult and fairly nonsensical to support uploading files through GraphQL.\n\u003c/Banner\u003e\n\nTo upload a file, use your collection's [`create`](../rest-api/overview#collections) endpoint. Send it all the data that your Collection requires, as well as a `file` key containing the file that you'd like to upload.\n\nSend your request as a `multipart/form-data` request, using [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) if possible.\n\n```ts\nconst fileInput = document.querySelector('#your-file-input') ;\nconst formData = new FormData();\n\nformData.append('file', fileInput.files[0]);\n\nfetch('api/:upload-slug', {\n method: 'POST',\n body: formData,\n /**\n * Do not manually add the Content-Type Header\n * the browser will handle this.\n *\n * headers: {\n * 'Content-Type': 'multipart/form-data'\n * }\n */\n})\n```\n\n## Access Control\n\nAll files that are uploaded to each Collection automatically support the `read` [Access Control](../access-control/overview) function from the Collection itself. You can use this to control who should be allowed to see your uploads, and who should not.\n"])</script><script>self.__next_f.push([1,"8e:T4260,"])</script><script>self.__next_f.push([1,"\nPayload offers additional storage adapters to handle file uploads. These adapters allow you to store files in different locations, such as Amazon S3, Vercel Blob Storage, Google Cloud Storage, and more.\n\n| Service | Package |\n| -------------------- |-------------------------------------------------------------------------------------------------------------------|\n| Vercel Blob | [`@payloadcms/storage-vercel-blob`](https://github.com/payloadcms/payload/tree/main/packages/storage-vercel-blob) |\n| AWS S3 | [`@payloadcms/storage-s3`](https://github.com/payloadcms/payload/tree/main/packages/storage-s3) |\n| Azure | [`@payloadcms/storage-azure`](https://github.com/payloadcms/payload/tree/main/packages/storage-azure) |\n| Google Cloud Storage | [`@payloadcms/storage-gcs`](https://github.com/payloadcms/payload/tree/main/packages/storage-gcs) |\n| Uploadthing | [`@payloadcms/storage-uploadthing`](https://github.com/payloadcms/payload/tree/main/packages/storage-uploadthing) |\n\n## Vercel Blob Storage\n[`@payloadcms/storage-vercel-blob`](https://www.npmjs.com/package/@payloadcms/storage-vercel-blob)\n\n### Installation#vercel-blob-installation\n\n```sh\npnpm add @payloadcms/storage-vercel-blob\n```\n\n### Usage#vercel-blob-usage\n\n- Configure the `collections` object to specify which collections should use the Vercel Blob adapter. The slug _must_ match one of your existing collection slugs.\n- Ensure you have `BLOB_READ_WRITE_TOKEN` set in your Vercel environment variables. This is usually set by Vercel automatically after adding blob storage to your project.\n- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.\n\n```ts\nimport { vercelBlobStorage } from '@payloadcms/storage-vercel-blob'\nimport { Media } from './collections/Media'\nimport { MediaWithPrefix } from './collections/MediaWithPrefix'\n\nexport default buildConfig({\n collections: [Media, MediaWithPrefix],\n plugins: [\n vercelBlobStorage({\n enabled: true, // Optional, defaults to true\n // Specify which collections should use Vercel Blob\n collections: {\n media: true,\n 'media-with-prefix': {\n prefix: 'my-prefix',\n },\n },\n // Token provided by Vercel once Blob storage is added to your Vercel project\n token: process.env.BLOB_READ_WRITE_TOKEN,\n }),\n ],\n})\n```\n\n### Configuration Options#vercel-blob-configuration\n\n| Option | Description | Default |\n| -------------------- | -------------------------------------------------------------------- | ----------------------------- |\n| `enabled` | Whether or not to enable the plugin | `true` |\n| `collections` | Collections to apply the Vercel Blob adapter to | |\n| `addRandomSuffix` | Add a random suffix to the uploaded file name in Vercel Blob storage | `false` |\n| `cacheControlMaxAge` | Cache-Control max-age in seconds | `365 * 24 * 60 * 60` (1 Year) |\n| `token` | Vercel Blob storage read/write token | `''` |\n\n## S3 Storage\n[`@payloadcms/storage-s3`](https://www.npmjs.com/package/@payloadcms/storage-s3)\n\n### Installation#s3-installation\n\n```sh\npnpm add @payloadcms/storage-s3\n```\n\n### Usage#s3-usage\n\n- Configure the `collections` object to specify which collections should use the S3 Storage adapter. The slug _must_ match one of your existing collection slugs.\n- The `config` object can be any [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object (from [`@aws-sdk/client-s3`](https://github.com/aws/aws-sdk-js-v3)). _This is highly dependent on your AWS setup_. Check the AWS documentation for more information.\n- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.\n\n```ts\nimport { s3Storage } from '@payloadcms/storage-s3'\nimport { Media } from './collections/Media'\nimport { MediaWithPrefix } from './collections/MediaWithPrefix'\n\nexport default buildConfig({\n collections: [Media, MediaWithPrefix],\n plugins: [\n s3Storage({\n collections: {\n media: true,\n 'media-with-prefix': {\n prefix,\n },\n },\n bucket: process.env.S3_BUCKET,\n config: {\n credentials: {\n accessKeyId: process.env.S3_ACCESS_KEY_ID,\n secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,\n },\n region: process.env.S3_REGION,\n // ... Other S3 configuration\n },\n }),\n ],\n})\n```\n\n### Configuration Options#s3-configuration\n\nSee the the [AWS SDK Package](https://github.com/aws/aws-sdk-js-v3) and [`S3ClientConfig`](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3) object for guidance on AWS S3 configuration.\n\n## Azure Blob Storage\n[`@payloadcms/storage-azure`](https://www.npmjs.com/package/@payloadcms/storage-azure)\n\n### Installation#azure-installation\n\n```sh\npnpm add @payloadcms/storage-azure\n```\n\n### Usage#azure-usage\n\n- Configure the `collections` object to specify which collections should use the Azure Blob adapter. The slug _must_ match one of your existing collection slugs.\n- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.\n\n```ts\nimport { azureStorage } from '@payloadcms/storage-azure'\nimport { Media } from './collections/Media'\nimport { MediaWithPrefix } from './collections/MediaWithPrefix'\n\nexport default buildConfig({\n collections: [Media, MediaWithPrefix],\n plugins: [\n azureStorage({\n collections: {\n media: true,\n 'media-with-prefix': {\n prefix,\n },\n },\n allowContainerCreate: process.env.AZURE_STORAGE_ALLOW_CONTAINER_CREATE === 'true',\n baseURL: process.env.AZURE_STORAGE_ACCOUNT_BASEURL,\n connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,\n containerName: process.env.AZURE_STORAGE_CONTAINER_NAME,\n }),\n ],\n})\n```\n\n### Configuration Options#azure-configuration\n\n| Option | Description | Default |\n| ---------------------- | ------------------------------------------------------------------------ | ------- |\n| `enabled` | Whether or not to enable the plugin | `true` |\n| `collections` | Collections to apply the Azure Blob adapter to | |\n| `allowContainerCreate` | Whether or not to allow the container to be created if it does not exist | `false` |\n| `baseURL` | Base URL for the Azure Blob storage account | |\n| `connectionString` | Azure Blob storage connection string | |\n| `containerName` | Azure Blob storage container name | |\n\n## Google Cloud Storage\n[`@payloadcms/storage-gcs`](https://www.npmjs.com/package/@payloadcms/storage-gcs)\n\n### Installation#gcs-installation\n\n```sh\npnpm add @payloadcms/storage-gcs\n```\n\n### Usage#gcs-usage\n\n- Configure the `collections` object to specify which collections should use the Google Cloud Storage adapter. The slug _must_ match one of your existing collection slugs.\n- When enabled, this package will automatically set `disableLocalStorage` to `true` for each collection.\n\n```ts\nimport { gcsStorage } from '@payloadcms/storage-gcs'\nimport { Media } from './collections/Media'\nimport { MediaWithPrefix } from './collections/MediaWithPrefix'\n\nexport default buildConfig({\n collections: [Media, MediaWithPrefix],\n plugins: [\n gcsStorage({\n collections: {\n media: true,\n 'media-with-prefix': {\n prefix,\n },\n },\n bucket: process.env.GCS_BUCKET,\n options: {\n apiEndpoint: process.env.GCS_ENDPOINT,\n projectId: process.env.GCS_PROJECT_ID,\n },\n }),\n ],\n})\n```\n\n### Configuration Options#gcs-configuration\n\n| Option | Description | Default |\n| ------------- | --------------------------------------------------------------------------------------------------- | --------- |\n| `enabled` | Whether or not to enable the plugin | `true` |\n| `collections` | Collections to apply the storage to | |\n| `bucket` | The name of the bucket to use | |\n| `options` | Google Cloud Storage client configuration. See [Docs](https://github.com/googleapis/nodejs-storage) | |\n| `acl` | Access control list for files that are uploaded | `Private` |\n\n\n## Uploadthing Storage\n[`@payloadcms/storage-uploadthing`](https://www.npmjs.com/package/@payloadcms/storage-uploadthing)\n\n### Installation#uploadthing-installation\n\n```sh\npnpm add @payloadcms/storage-uploadthing\n```\n\n### Usage#uploadthing-usage\n\n- Configure the `collections` object to specify which collections should use uploadthing. The slug _must_ match one of your existing collection slugs and be an `upload` type.\n- Get a token from Uploadthing and set it as `token` in the `options` object.\n- `acl` is optional and defaults to `public-read`.\n\n```ts\nexport default buildConfig({\n collections: [Media],\n plugins: [\n uploadthingStorage({\n collections: {\n media: true,\n },\n options: {\n token: process.env.UPLOADTHING_TOKEN,\n acl: 'public-read',\n },\n }),\n ],\n})\n```\n\n### Configuration Options#uploadthing-configuration\n\n| Option | Description | Default |\n| ---------------- | ----------------------------------------------- | ------------- |\n| `token` | Token from Uploadthing. Required. | |\n| `acl` | Access control list for files that are uploaded | `public-read` |\n| `logLevel` | Log level for Uploadthing | `info` |\n| `fetch` | Custom fetch function | `fetch` |\n| `defaultKeyType` | Default key type for file operations | `fileKey` |\n\n\n## Custom Storage Adapters\n\nIf you need to create a custom storage adapter, you can use the [`@payloadcms/plugin-cloud-storage`](https://www.npmjs.com/package/@payloadcms/plugin-cloud-storage) package. This package is used internally by the storage adapters mentioned above.\n\n### Installation#custom-installation\n\n`pnpm add @payloadcms/plugin-cloud-storage`\n\n### Usage#custom-usage\n\nReference any of the existing storage adapters for guidance on how this should be structured. Create an adapter following the `GeneratedAdapter` interface. Then, pass the adapter to the `cloudStorage` plugin.\n\n```ts\nexport interface GeneratedAdapter {\n /**\n * Additional fields to be injected into the base collection and image sizes\n */\n fields?: Field[]\n /**\n * Generates the public URL for a file\n */\n generateURL?: GenerateURL\n handleDelete: HandleDelete\n handleUpload: HandleUpload\n name: string\n onInit?: () =\u003e void\n staticHandler: StaticHandler\n}\n```\n\n```ts\nimport { buildConfig } from 'payload'\nimport { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'\n\nexport default buildConfig({\n plugins: [\n cloudStorage({\n collections: {\n 'my-collection-slug': {\n adapter: theAdapterToUse, // see docs for the adapter you want to use\n },\n },\n }),\n ],\n // The rest of your config goes here\n})\n```\n\n## Plugin options\n\nThis plugin is configurable to work across many different Payload collections. A `*` denotes that the property is required.\n\n| Option | Type | Description |\n| ---------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |\n| `collections` \\* | `Record\u003cstring, CollectionOptions\u003e` | Object with keys set to the slug of collections you want to enable the plugin for, and values set to collection-specific options. |\n| `enabled` | `boolean` | To conditionally enable/disable plugin. Default: `true`. |\n\n## Collection-specific options\n\n| Option | Type | Description |\n| ----------------------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `adapter` \\* | [Adapter](https://github.com/payloadcms/plugin-cloud-storage/blob/master/src/types.ts#L51) | Pass in the adapter that you'd like to use for this collection. You can also set this field to `null` for local development if you'd like to bypass cloud storage in certain scenarios and use local storage. |\n| `disableLocalStorage` | `boolean` | Choose to disable local storage on this collection. Defaults to `true`. |\n| `disablePayloadAccessControl` | `true` | Set to `true` to disable Payload's Access Control. [More](#payload-access-control) |\n| `prefix` | `string` | Set to `media/images` to upload files inside `media/images` folder in the bucket. |\n| `generateFileURL` | [GenerateFileURL](https://github.com/payloadcms/plugin-cloud-storage/blob/master/src/types.ts#L53) | Override the generated file URL with one that you create. |\n\n## Payload Access Control\n\nPayload ships with [Access Control](../access-control/overview) that runs _even on statically served files_. The same `read` Access Control property on your `upload`-enabled collections is used, and it allows you to restrict who can request your uploaded files.\n\nTo preserve this feature, by default, this plugin _keeps all file URLs exactly the same_. Your file URLs won't be updated to point directly to your cloud storage source, as in that case, Payload's Access control will be completely bypassed and you would need public readability on your cloud-hosted files.\n\nInstead, all uploads will still be reached from the default `/collectionSlug/staticURL/filename` path. This plugin will \"pass through\" all files that are hosted on your third-party cloud service—with the added benefit of keeping your existing Access Control in place.\n\nIf this does not apply to you (your upload collection has `read: () =\u003e true` or similar) you can disable this functionality by setting `disablePayloadAccessControl` to `true`. When this setting is in place, this plugin will update your file URLs to point directly to your cloud host.\n\n## Conditionally Enabling/Disabling\n\nThe proper way to conditionally enable/disable this plugin is to use the `enabled` property.\n\n```ts\ncloudStoragePlugin({\n enabled: process.env.MY_CONDITION === 'true',\n collections: {\n 'my-collection-slug': {\n adapter: theAdapterToUse, // see docs for the adapter you want to use\n },\n },\n}),\n```\n"])</script><script>self.__next_f.push([1,"8f:T1cfa,"])</script><script>self.__next_f.push([1,"\n## Introduction\n\nPayload has a few email adapters that can be imported to enable email functionality. The [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) package will be the package most will want to install. This package provides an easy way to use [Nodemailer](https://nodemailer.com) for email and won't get in your way for those already familiar.\n\nThe email adapter should be passed into the `email` property of the Payload Config. This will allow Payload to send [auth-related emails](../authentication/email) for things like password resets, new user verification, and any other email sending needs you may have.\n\n## Configuration\n\n### Default Configuration\n\nWhen email is not needed or desired, Payload will log a warning on startup notifying that email is not configured. A warning message will also be logged on any attempt to send an email.\n\n### Email Adapter\n\nAn email adapter will require at least the following fields:\n\n| Option | Description |\n| --------------------------- | -------------------------------------------------------------------------------- |\n| **`defaultFromName`** \\* | The name part of the From field that will be seen on the delivered email |\n| **`defaultFromAddress`** \\* | The email address part of the From field that will be used when delivering email |\n\n\n### Official Email Adapters\n\n| Name | Package | Description |\n| ---------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Nodemailer | [@payloadcms/email-nodemailer](https://www.npmjs.com/package/@payloadcms/email-nodemailer) | Use any [Nodemailer transport](https://nodemailer.com/transports), including SMTP, Resend, SendGrid, and more. This was provided by default in Payload 2.x. This is the easiest migration path. |\n| Resend | [@payloadcms/email-resend](https://www.npmjs.com/package/@payloadcms/email-resend) | Resend email via their REST API. This is preferred for serverless platforms such as Vercel because it is much more lightweight than the nodemailer adapter. |\n\n## Nodemailer Configuration\n\n| Option | Description |\n| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **`transport`** | The Nodemailer transport object for when you want to do it yourself, not needed when transportOptions is set |\n| **`transportOptions`** | An object that configures the transporter that Payload will create. For all the available options see the [Nodemailer documentation](https://nodemailer.com) or see the examples below |\n\n## Use SMTP\n\nSimple Mail Transfer Protocol (SMTP) options can be passed in using the `transportOptions` object on the `email` options. See the [Nodemailer SMTP documentation](https://nodemailer.com/smtp/) for more information, including details on when `secure` should and should not be set to `true`.\n\n**Example email options using SMTP:**\n\n```ts\nimport { buildConfig } from 'payload'\nimport { nodemailerAdapter } from '@payloadcms/email-nodemailer'\n\nexport default buildConfig({\n email: nodemailerAdapter({\n defaultFromAddress: 'info@payloadcms.com',\n defaultFromName: 'Payload',\n // Nodemailer transportOptions\n transportOptions: {\n host: process.env.SMTP_HOST,\n port: 587,\n auth: {\n user: process.env.SMTP_USER,\n pass: process.env.SMTP_PASS,\n },\n },\n }),\n})\n```\n\n**Example email options using nodemailer.createTransport:**\n\n```ts\nimport { buildConfig } from 'payload'\nimport { nodemailerAdapter } from '@payloadcms/email-nodemailer'\nimport nodemailer from 'nodemailer'\n\nexport default buildConfig({\n email: nodemailerAdapter({\n defaultFromAddress: 'info@payloadcms.com',\n defaultFromName: 'Payload',\n // Any Nodemailer transport can be used\n transport: nodemailer.createTransport({\n host: process.env.SMTP_HOST,\n port: 587,\n auth: {\n user: process.env.SMTP_USER,\n pass: process.env.SMTP_PASS,\n },\n }),\n }),\n})\n```\n\n**Custom Transport:**\n\nYou also have the ability to bring your own nodemailer transport. This is an example of using the SendGrid nodemailer transport.\n\n\n```ts\nimport { buildConfig } from 'payload'\nimport { nodemailerAdapter } from '@payloadcms/email-nodemailer'\nimport nodemailerSendgrid from 'nodemailer-sendgrid'\n\n\nexport default buildConfig({\n email: nodemailerAdapter({\n defaultFromAddress: 'info@payloadcms.com',\n defaultFromName: 'Payload',\n transportOptions: nodemailerSendgrid({\n apiKey: process.env.SENDGRID_API_KEY,\n }),\n }),\n})\n```\n\nDuring development, if you pass nothing to `nodemailerAdapter`, it will use the [ethereal.email](https://ethereal.email) service.\n\nThis will log the ethereal.email details to console on startup.\n\n```ts\nimport { nodemailerAdapter } from '@payloadcms/email-nodemailer'\n\nexport default buildConfig({\n email: nodemailerAdapter(),\n})\n```\n\n## Resend Configuration\n\nThe Resend adapter requires an API key to be passed in the options. This can be found in the Resend dashboard. This is the preferred package if you are deploying on Vercel because this is much more lightweight than the Nodemailer adapter.\n\n| Option | Description |\n| ------ | ----------------------------------- |\n| apiKey | The API key for the Resend service. |\n\n```ts\nimport { buildConfig } from 'payload'\nimport { resendAdapter } from '@payloadcms/email-resend'\n\nexport default buildConfig({\n email: resendAdapter({\n defaultFromAddress: 'dev@payloadcms.com',\n defaultFromName: 'Payload CMS',\n apiKey: process.env.RESEND_API_KEY || '',\n }),\n})\n```\n\n## Sending Mail\n\nWith a working transport you can call it anywhere you have access to Payload by calling `payload.sendEmail(message)`. The `message` will contain the `to`, `subject` and `html` or `text` for the email being sent. Other options are also available and can be seen in the sendEmail args. Support for these will depend on the adapter being used.\n\n```ts\n// Example of sending an email\nconst email = await payload.sendEmail({\n to: 'test@example.com',\n subject: 'This is a test email',\n text: 'This is my message body',\n})\n```\n\n## Using multiple mail providers\n\nPayload supports the use of a single transporter of email, but there is nothing stopping you from having more. Consider a use case where sending bulk email is handled differently than transactional email and could be done using a [hook](../hooks/overview).\n"])</script><script>self.__next_f.push([1,"90:Tee4,"])</script><script>self.__next_f.push([1,"\nPayload's Jobs Queue gives you a simple, yet powerful way to offload large or future tasks to separate compute resources which is a very powerful feature of many application frameworks.\n\n### Example use cases\n\n**Non-blocking workloads**\n\nYou might need to perform some complex, slow-running logic in a Payload [Hook](../hooks/overview) but you don't want that hook to \"block\" or slow down the response returned from the Payload API. Instead of running this logic directly in a hook, which would block your API response from returning until the expensive work is completed, you can queue a new Job and let it run at a later date.\n\nExamples:\n\n- Create vector embeddings from your documents, and keep them in sync as your documents change\n- Send data to a third-party API on document change\n- Trigger emails based on customer actions\n\n**Scheduled actions**\n\nIf you need to schedule an action to be run or processed at a certain date in the future, you can queue a job with the `waitUntil` property set. This will make it so the job is not \"picked up\" until that `waitUntil` date has passed.\n\nExamples:\n\n- Process scheduled posts, where the scheduled date is at a time set in the future\n- Unpublish posts at a given time\n- Send a reminder email to a customer after X days of signing up for a trial\n\n**Periodic sync or similar scheduled action**\n\nSome applications may need to perform a regularly scheduled operation of some type. Jobs are perfect for this because you can execute their logic using `cron`, scheduled nightly, every twelve hours, or some similar time period.\n\nExamples:\n\n- You'd like to send emails to all customers on a regular, scheduled basis\n- Periodically trigger a rebuild of your frontend at night\n- Sync resources to or from a third-party API during non-peak times\n\n**Offloading complex operations**\n\nYou may run into the need to perform computationally expensive functions which might slow down your main Payload API server(s). The Jobs Queue allows you to offload these tasks a separate compute resource rather than slowing down the server(s) that run your Payload APIs. With Payload Task definitions, you can even keep large dependencies out of your main Next.js bundle by dynamically importing them only when they are used. This keeps your Next.js + Payload compilation fast and ensures large dependencies do not get bundled into your Payload production build.\n\nExamples:\n\n- You need to create (and then keep in sync) vector embeddings of your documents as they change, but you use an open source model to generate embeddings\n- You have a PDF generator that needs to dynamically build and send PDF versions of documents to customers\n- You need to use a headless browser to perform some type of logic\n- You need to perform a series of actions, each of which depends on a prior action and should be run in as \"durable\" of a fashion as possible\n\n### How it works\n\nThere are a few concepts that you should become familiarized with before using Payload's Jobs Queue. We recommend learning what each of these does in order to fully understand how to leverage the power of Payload's Jobs Queue.\n\n1. [Tasks](../jobs-queue/tasks)\n1. [Workflows](../jobs-queue/workflows)\n1. [Jobs](../jobs-queue/jobs)\n1. [Queues](../jobs-queue/queues)\n\nAll of these pieces work together in order to allow you to offload long-running, expensive, or future scheduled work from your main APIs.\n\nHere's a quick overview:\n\n- A Task is a specific function that performs business logic\n- Workflows are groupings of specific tasks which should be run in-order, and can be retried from a specific point of failure\n- A Job is an instance of a single task or workflow which will be executed\n- A Queue is a way to segment your jobs into different \"groups\" - for example, some to run nightly, and others to run every 10 minutes\n"])</script><script>self.__next_f.push([1,"91:T13b1,"])</script><script>self.__next_f.push([1,"\n\u003cBanner type=\"default\"\u003e\n A \u003cstrong\u003e\"Task\"\u003c/strong\u003e is a function definition that performs business logic and whose input and output are both strongly typed.\n\u003c/Banner\u003e\n\nYou can register Tasks on the Payload config, and then create [Jobs](../jobs-queue/jobs) or [Workflows](../jobs-queue/workflows) that use them. Think of Tasks like tidy, isolated \"functions that do one specific thing\".\n\nPayload Tasks can be configured to automatically retried if they fail, which makes them valuable for \"durable\" workflows like AI applications where LLMs can return non-deterministic results, and might need to be retried.\n\nTasks can either be defined within the `jobs.tasks` array in your payload config, or they can be defined inline within a workflow.\n\n### Defining tasks in the config\n\nSimply add a task to the `jobs.tasks` array in your Payload config. A task consists of the following fields:\n\n| Option | Description |\n| --------------------------- | -------------------------------------------------------------------------------- |\n| `slug` | Define a slug-based name for this job. This slug needs to be unique among both tasks and workflows.|\n| `handler` | The function that should be responsible for running the job. You can either pass a string-based path to the job function file, or the job function itself. If you are using large dependencies within your job, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. |\n| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |\n| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this task. By default, this is \"Task\" + the capitalized task slug. |\n| `outputSchema` | Define the output field schema - payload will generate a type for this schema. |\n| `label` | Define a human-friendly label for this task. |\n| `onFail` | Function to be executed if the task fails. |\n| `onSuccess` | Function to be executed if the task succeeds. |\n| `retries` | Specify the number of times that this step should be retried if it fails. |\n\nThe logic for the Task is defined in the `handler` - which can be defined as a function, or a path to a function. The `handler` will run once a worker picks picks up a Job that includes this task.\n\nIt should return an object with an `output` key, which should contain the output of the task as you've defined.\n\nExample:\n\n```ts\nexport default buildConfig({\n // ...\n jobs: {\n tasks: [\n {\n // Configure this task to automatically retry\n // up to two times\n retries: 2,\n\n // This is a unique identifier for the task\n\n slug: 'createPost',\n\n // These are the arguments that your Task will accept\n inputSchema: [\n {\n name: 'title',\n type: 'text',\n required: true,\n },\n ],\n\n // These are the properties that the function should output\n outputSchema: [\n {\n name: 'postID',\n type: 'text',\n required: true,\n },\n ],\n\n // This is the function that is run when the task is invoked\n handler: async ({ input, job, req }) =\u003e {\n const newPost = await req.payload.create({\n collection: 'post',\n req,\n data: {\n title: input.title,\n },\n })\n return {\n output: {\n postID: newPost.id,\n },\n }\n },\n } as TaskConfig\u003c'createPost'\u003e,\n ]\n }\n})\n```\n\nIn addition to defining handlers as functions directly provided to your Payload config, you can also pass an _absolute path_ to where the handler is defined. If your task has large dependencies, and you are planning on executing your jobs in a separate process that has access to the filesystem, this could be a handy way to make sure that your Payload + Next.js app remains quick to compile and has minimal dependencies.\n\nIn general, this is an advanced use case. Here's how this would look:\n\n`payload.config.ts:`\n\n```ts\nimport { fileURLToPath } from 'node:url'\nimport path from 'path'\n\nconst filename = fileURLToPath(import.meta.url)\nconst dirname = path.dirname(filename)\n\nexport default buildConfig({\n jobs: {\n tasks: [\n {\n // ...\n // The #createPostHandler is a named export within the `createPost.ts` file\n handler: path.resolve(dirname, 'src/tasks/createPost.ts') + '#createPostHandler',\n }\n ]\n }\n})\n```\n\nThen, the `createPost` file itself:\n\n`src/tasks/createPost.ts:`\n\n```ts\nimport type { TaskHandler } from 'payload'\n\nexport const createPostHandler: TaskHandler\u003c'createPost'\u003e = async ({ input, job, req }) =\u003e {\n const newPost = await req.payload.create({\n collection: 'post',\n req,\n data: {\n title: input.title,\n },\n })\n return {\n output: {\n postID: newPost.id,\n },\n }\n}\n```\n"])</script><script>self.__next_f.push([1,"92:T167e,"])</script><script>self.__next_f.push([1,"\n\u003cBanner type=\"default\"\u003e\n A \u003cstrong\u003e\"Workflow\"\u003c/strong\u003e is an optional way to \u003cem\u003ecombine multiple tasks together\u003c/em\u003e in a way that can be gracefully retried from the point of failure.\n\u003c/Banner\u003e\n\nThey're most helpful when you have multiple tasks in a row, and you want to configure each task to be able to be retried if they fail.\n\nIf a task within a workflow fails, the Workflow will automatically \"pick back up\" on the task where it failed and **not re-execute any prior tasks that have already been executed**.\n\n#### Defining a workflow\n\nThe most important aspect of a Workflow is the `handler`, where you can declare when and how the tasks should run by simply calling the `runTask` function. If any task within the workflow, fails, the entire `handler` function will re-run.\n\nHowever, importantly, tasks that have successfully been completed will simply re-return the cached and saved output without running again. The Workflow will pick back up where it failed and only task from the failure point onward will be re-executed.\n\nTo define a JS-based workflow, simply add a workflow to the `jobs.wokflows` array in your Payload config. A workflow consists of the following fields:\n\n| Option | Description |\n| --------------------------- | -------------------------------------------------------------------------------- |\n| `slug` | Define a slug-based name for this workflow. This slug needs to be unique among both tasks and workflows.|\n| `handler` | The function that should be responsible for running the workflow. You can either pass a string-based path to the workflow function file, or workflow job function itself. If you are using large dependencies within your workflow, you might prefer to pass the string path because that will avoid bundling large dependencies in your Next.js app. |\n| `inputSchema` | Define the input field schema - payload will generate a type for this schema. |\n| `interfaceName` | You can use interfaceName to change the name of the interface that is generated for this workflow. By default, this is \"Workflow\" + the capitalized workflow slug. |\n| `label` | Define a human-friendly label for this workflow. |\n| `queue` | Optionally, define the queue name that this workflow should be tied to. Defaults to \"default\". |\n\nExample:\n\n```ts\nexport default buildConfig({\n // ...\n jobs: {\n tasks: [\n // ...\n ]\n workflows: [\n {\n slug: 'createPostAndUpdate',\n\n // The arguments that the workflow will accept\n inputSchema: [\n {\n name: 'title',\n type: 'text',\n required: true,\n },\n ],\n\n // The handler that defines the \"control flow\" of the workflow\n // Notice how it uses the `tasks` argument to execute your predefined tasks.\n // These are strongly typed!\n handler: async ({ job, tasks }) =\u003e {\n\n // This workflow first runs a task called `createPost`.\n\n // You need to define a unique ID for this task invocation\n // that will always be the same if this workflow fails\n // and is re-executed in the future. Here, we hard-code it to '1'\n const output = await tasks.createPost('1', {\n input: {\n title: job.input.title,\n },\n })\n\n // Once the prior task completes, it will run a task\n // called `updatePost`\n await tasks.updatePost('2', {\n input: {\n post: job.taskStatus.createPost['1'].output.postID, // or output.postID\n title: job.input.title + '2',\n },\n })\n },\n } as WorkflowConfig\u003c'updatePost'\u003e\n ]\n }\n})\n```\n\n#### Running tasks inline\n\nIn the above example, our workflow was executing tasks that we already had defined in our Payload config. But, you can also run tasks without predefining them.\n\nTo do this, you can use the `inlineTask` function.\n\nThe drawbacks of this approach are that tasks cannot be re-used across workflows as easily, and the **task data stored in the job** will not be typed. In the following example, the inline task data will be stored on the job under `job.taskStatus.inline['2']` but completely untyped, as types for dynamic tasks like these cannot be generated beforehand.\n\nExample:\n\n```ts\nexport default buildConfig({\n // ...\n jobs: {\n tasks: [\n // ...\n ]\n workflows: [\n {\n slug: 'createPostAndUpdate',\n inputSchema: [\n {\n name: 'title',\n type: 'text',\n required: true,\n },\n ],\n handler: async ({ job, tasks, inlineTask }) =\u003e {\n // Here, we run a predefined task.\n // The `createPost` handler arguments and return type\n // are both strongly typed\n const output = await tasks.createPost('1', {\n input: {\n title: job.input.title,\n },\n })\n\n // Here, this task is not defined in the Payload config\n // and is \"inline\". Its output will be stored on the Job in the database\n // however its arguments will be untyped.\n const { newPost } = await inlineTask('2', {\n task: async ({ req }) =\u003e {\n const newPost = await req.payload.update({\n collection: 'post',\n id: '2',\n req,\n retries: 3,\n data: {\n title: 'updated!',\n },\n })\n return {\n output: {\n newPost\n },\n }\n },\n })\n },\n } as WorkflowConfig\u003c'updatePost'\u003e\n ]\n }\n})\n```\n"])</script><script>self.__next_f.push([1,"93:T60f,\nNow that we have covered Tasks and Workflows, we can tie them together with a concept called a Job.\n\n\u003cBanner type=\"default\"\u003e\n Whereas you define Workflows and Tasks, which control your business logic, a \u003cstrong\u003eJob\u003c/strong\u003e is an individual instance of either a Task or a Workflow which contains many tasks.\n\u003c/Banner\u003e\n\nFor example, let's say we have a Workflow or Task that describes the logic to sync information from Payload to a third-party system. This is how you'd declare how to sync that info, but it wouldn't do anything on its own. In order to run that task or workflow, you'd create a Job that references the corresponding Task or Workflow.\n\nJobs are stored in the Payload database in the `payload-jobs` collection, and you can decide to keep a running list of all jobs, or configure Payload to delete the job when it has been successfully executed.\n\n#### Queuing a new job\n\nIn order to queue a job, you can use the `payload.jobs.queue` function.\n\nHere's how you'd queue a new Job, which will run a `createPostAndUpdate` workflow:\n\n```ts\nconst createdJob = await payload.jobs.queue({\n // Pass the name of the workflow\n workflow: 'createPostAndUpdate',\n // The input type will be automatically typed\n // according to the input you've defined for this workflow\n input: {\n title: 'my title',\n },\n})\n```\n\nIn addition to being able to queue new Jobs based on Workflows, you can also queue a job for a single Task:\n\n```ts\nconst createdJob = await payload.jobs.queue({\n task: 'createPost',\n input: {\n title: 'my title',\n },\n})\n```\n94:T10e3,"])</script><script>self.__next_f.push([1,"\nQueues are the final aspect of Payload's Jobs Queue and deal with how to _run your jobs_. Up to this point, all we've covered is how to queue up jobs to run, but so far, we aren't actually running any jobs.\n\n\u003cBanner type=\"default\"\u003e\n A \u003cstrong\u003eQueue\u003c/strong\u003e is a grouping of jobs that should be executed in order of when they were added.\n\u003c/Banner\u003e\n\nWhen you go to run jobs, Payload will query for any jobs that are added to the queue and then run them. By default, all queued jobs are added to the `default` queue.\n\n**But, imagine if you wanted to have some jobs that run nightly, and other jobs which should run every five minutes.**\n\nBy specifying the `queue` name when you queue a new job using `payload.jobs.queue()`, you can queue certain jobs with `queue: 'nightly'`, and other jobs can be left as the default queue.\n\nThen, you could configure two different runner strategies:\n\n1. A `cron` that runs nightly, querying for jobs added to the `nightly` queue\n2. Another that runs any jobs that were added to the `default` queue every ~5 minutes or so\n\n## Executing jobs\n\nAs mentioned above, you can queue jobs, but the jobs won't run unless a worker picks up your jobs and runs them. This can be done in two ways:\n\n#### Endpoint\n\nYou can execute jobs by making a fetch request to the `/api/payload-jobs/run` endpoint:\n\n```ts\n// Here, we're saying we want to run only 100 jobs for this invocation\n// and we want to pull jobs from the `nightly` queue:\nawait fetch('/api/payload-jobs/run?limit=100\u0026queue=nightly', {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n});\n```\n\nThis endpoint is automatically mounted for you and is helpful in conjunction with serverless platforms like Vercel, where you might want to use Vercel Cron to invoke a serverless function that executes your jobs.\n\n**Vercel Cron Example**\n\nIf you're deploying on Vercel, you can add a `vercel.json` file in the root of your project that configures Vercel Cron to invoke the `run` endpoint on a cron schedule.\n\nHere's an example of what this file will look like:\n\n```json\n{\n \"crons\": [\n {\n \"path\": \"/api/payload-jobs/run\",\n \"schedule\": \"*/5 * * * *\"\n }\n ]\n}\n```\n\nThe configuration above schedules the endpoint `/api/payload-jobs/run` to be invoked every 5 minutes.\n\nThe last step will be to secure your `run` endpoint so that only the proper users can invoke the runner.\n\nTo do this, you can set an environment variable on your Vercel project called `CRON_SECRET`, which should be a random string—ideally 16 characters or longer.\n\nThen, you can modify the `access` function for running jobs by ensuring that only Vercel can invoke your runner.\n\n```ts\nexport default buildConfig({\n // Other configurations...\n jobs: {\n access: {\n run: ({ req }: { req: PayloadRequest }): boolean =\u003e {\n // Allow logged in users to execute this endpoint (default)\n if (req.user) return true\n\n // If there is no logged in user, then check\n // for the Vercel Cron secret to be present as an\n // Authorization header:\n const authHeader = req.headers.get('authorization');\n return authHeader === `Bearer ${process.env.CRON_SECRET}`;\n },\n },\n // Other job configurations...\n }\n})\n```\n\nThis works because Vercel automatically makes the `CRON_SECRET` environment variable available to the endpoint as the `Authorization` header when triggered by the Vercel Cron, ensuring that the jobs can be run securely.\n\nAfter the project is deployed to Vercel, the Vercel Cron job will automatically trigger the `/api/payload-jobs/run` endpoint in the specified schedule, running the queued jobs in the background.\n\n#### Local API\n\nIf you want to process jobs programmatically from your server-side code, you can use the Local API:\n\n```ts\nconst results = await payload.jobs.run()\n\n// You can customize the queue name and limit by passing them as arguments:\nawait payload.jobs.run({ queue: 'nightly', limit: 100 })\n```\n\n#### Bin script\n\nFinally, you can process jobs via the bin script that comes with Payload out of the box.\n\n```sh\nnpx payload jobs:run --queue default --limit 10\n```\n\nIn addition, the bin script allows you to pass a `--cron` flag to the `jobs:run` command to run the jobs on a scheduled, cron basis:\n\n```sh\nnpx payload jobs:run --cron \"*/5 * * * *\"\n```\n"])</script><script>self.__next_f.push([1,"95:T4ec,\nPayload supports TypeScript natively, and not only that, the entirety of the CMS is built with TypeScript. To get started developing with Payload and TypeScript, you can use one of Payload's built-in boilerplates in one line via `create-payload-app`:\n\n```\nnpx create-payload-app@latest\n```\n\nPick a TypeScript project type to get started easily.\n\n## Setting up from Scratch\n\nIt's also possible to set up a TypeScript project from scratch. We plan to write up a guide for exactly how—so keep an eye out for that, too.\n\n## Using Payload's Exported Types\n\nPayload exports a number of types that you may find useful while writing your own custom functionality like [Plugins](../plugins/overview), [Hooks](../hooks/overview), [Access Control](../access-control/overview) functions, [Custom Views](../admin/views), [GraphQL queries / mutations](../graphql/overview) or anything else.\n\n## Config Types\n\n- [Base config](../configuration/overview#typescript)\n- [Collections](../configuration/collections#typescript)\n- [Globals](../configuration/globals#typescript)\n- [Fields](../fields/overview#typescript)\n\n## Hook Types\n\n- [Collection hooks](../hooks/collections#typescript)\n- [Global hooks](../hooks/globals#typescript)\n- [Field hooks](../hooks/fields#typescript)\n96:T18aa,"])</script><script>self.__next_f.push([1,"\nWhile building your own custom functionality into Payload, like [Plugins](../plugins/overview), [Hooks](../hooks/overview), [Access Control](../access-control/overview) functions, [Custom Views](../admin/views), [GraphQL queries / mutations](../graphql/overview), or anything else, you may benefit from generating your own TypeScript types dynamically from your Payload Config itself.\n\n## Types generation script\n\nRun the following command in a Payload project to generate types based on your Payload Config:\n\n```\npayload generate:types\n```\n\nYou can run this command whenever you need to regenerate your types, and then you can use these types in your Payload code directly.\n\n## Disable declare statement\n\nBy default, `generate:types` will add a `declare` statement to your types file, which automatically enables type inference within Payload.\n\nIf you are using your `payload-types.ts` file in other repos, though, it might be better to disable this `declare` statement, so that you don't get any TS errors in projects that use your Payload types, but do not have Payload installed.\n\n```ts\n// payload.config.ts\n{\n // ...\n typescript: {\n declare: false, // defaults to true if not set\n },\n}\n```\n\nIf you do disable the `declare` pattern, you'll need to manually add a `declare` statement to your code in order for Payload types to be recognized. Here's an example showing how to declare your types in your `payload.config.ts` file:\n\n```ts\nimport { Config } from './payload-types'\n\ndeclare module 'payload' {\n export interface GeneratedTypes extends Config {}\n}\n```\n\n## Custom output file path\n\nYou can specify where you want your types to be generated by adding a property to your Payload Config:\n\n```ts\n// payload.config.ts\n{\n // ...\n\ttypescript: {\n // defaults to: path.resolve(__dirname, './payload-types.ts')\n\t\toutputFile: path.resolve(__dirname, './generated-types.ts'),\n\t},\n}\n```\n\nThe above example places your types next to your Payload Config itself as the file `generated-types.ts`.\n\n## Custom generated types\n\nPayload generates your types based on a JSON schema. You can extend that JSON schema, and thus the generated types, by passing a function to `typescript.schema`:\n\n```ts\n// payload.config.ts\n{\n // ...\n typescript: {\n schema: [\n ({ jsonSchema }) =\u003e {\n // Modify the JSON schema here\n jsonSchema.definitions.Test = {\n type: 'object',\n properties: {\n title: { type: 'string' },\n content: { type: 'string' },\n },\n required: ['title', 'content'],\n }\n return jsonSchema\n },\n ]\n }\n}\n\n// This will generate the following type in your payload-types.ts:\n\nexport interface Test {\n title: string;\n content: string;\n [k: string]: unknown;\n}\n```\n\nThis function takes the existing JSON schema as an argument and returns the modified JSON schema. It can be useful for plugins that wish to generate their own types.\n\n## Example Usage\n\nFor example, let's look at the following simple Payload Config:\n\n```ts\nimport type { Config } from 'payload'\n\nconst config: Config = {\n serverURL: process.env.NEXT_PUBLIC_SERVER_URL,\n admin: {\n user: 'users',\n },\n collections: [\n {\n slug: 'users',\n fields: [\n {\n name: 'name',\n type: 'text',\n required: true,\n }\n ]\n },\n {\n slug: 'posts',\n admin: {\n useAsTitle: 'title',\n },\n fields: [\n {\n name: 'title',\n type: 'text',\n },\n {\n name: 'author',\n type: 'relationship',\n relationTo: 'users',\n },\n ]\n }\n ]\n}\n```\n\nBy generating types, we'll end up with a file containing the following two TypeScript interfaces:\n\n```ts\nexport interface User {\n id: string\n name: string\n email?: string\n resetPasswordToken?: string\n resetPasswordExpiration?: string\n loginAttempts?: number\n lockUntil?: string\n}\n\nexport interface Post {\n id: string\n title?: string\n author?: string | User\n}\n```\n\n## Custom Field Interfaces\n\nFor `array`, `block`, `group` and named `tab` fields, you can generate top level reusable interfaces. The following group field config:\n\n```ts\n{\n type: 'group',\n name: 'meta',\n interfaceName: 'SharedMeta', \u003c-- here!!\n fields: [\n {\n name: 'title',\n type: 'text',\n },\n {\n name: 'description',\n type: 'text',\n },\n ],\n}\n```\n\nwill generate:\n\n```ts\n// a top level reusable interface!!\nexport interface SharedMeta {\n title?: string\n description?: string\n}\n\n// example usage inside collection interface\nexport interface Collection1 {\n // ...other fields\n meta?: SharedMeta\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNaming Collisions\u003c/strong\u003e\n \u003cbr /\u003e\n Since these types are hoisted to the top level, you need to be aware that naming collisions can\n occur. For example, if you have a collection with the name of `Meta` and you also create a\n interface with the name `Meta` they will collide. It is recommended to scope your interfaces by\n appending the field type to the end, i.e. `MetaGroup` or similar.\n\u003c/Banner\u003e\n\n## Using your types\n\nNow that your types have been generated, payloads local API will now be typed. It is common for users to want to use this in their frontend code, we recommend generating them with Payload and then copying the file over to your frontend codebase. This is the simplest way to get your types into your frontend codebase.\n\n### Adding an NPM script\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eImportant\u003c/strong\u003e\n \u003cbr /\u003e\n Payload needs to be able to find your config to generate your types.\n\u003c/Banner\u003e\n\nPayload will automatically try and locate your config, but might not always be able to find it. For example, if you are working in a `/src` directory or similar, you need to tell Payload where to find your config manually by using an environment variable. If this applies to you, you can create an NPM script to make generating your types easier.\n\nTo add an NPM script to generate your types and show Payload where to find your config, open your `package.json` and update the `scripts` property to the following:\n\n```\n{\n \"scripts\": {\n \"generate:types\": \"PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types\",\n },\n}\n```\n\nNow you can run `yarn generate:types` to easily generate your types.\n"])</script><script>self.__next_f.push([1,"97:T19d2,"])</script><script>self.__next_f.push([1,"\nPayload Plugins take full advantage of the modularity of the [Payload Config](../configuration/overview), allowing developers developers to easily inject custom—sometimes complex—functionality into Payload apps from a very small touch-point. This is especially useful is sharing your work across multiple projects or with the greater Payload community.\n\nThere are many [Official Plugins](#official-plugins) available that solve for some of the most common uses cases, such as the [Form Builder Plugin](./form-builder) or [SEO Plugin](./seo). There are also [Community Plugins](#community-plugins) available, maintained entirely by contributing members. To extend Payload's functionality in some other way, you can easily [build your own plugin](./build-your-own).\n\nTo configure Plugins, use the `plugins` property in your [Payload Config](../configuration/overview):\n\n```ts\nimport { buildConfig } from 'payload'\n\nconst config = buildConfig({\n // ...\n // highlight-start\n plugins: [\n // Add Plugins here\n ],\n // highlight-end\n})\n```\n\nWriting Plugins is no more complex than writing regular JavaScript. If you know the basic concept of [callback functions](https://developer.mozilla.org/en-US/docs/Glossary/Callback_function) or how [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) works, and are up to speed with Payload concepts, then writing a plugin will be a breeze.\n\n\u003cBanner type=\"success\"\u003e\n Because we rely on a simple config-based structure, Payload Plugins simply take in an\n existing config and returns a _modified_ config with new fields, hooks, collections, admin views, or\n anything else you can think of.\n\u003c/Banner\u003e\n\n**Example use cases:**\n\n- Automatically sync data from a specific collection to HubSpot or a similar CRM when data is added or changes\n- Add password-protection functionality to certain documents\n- Add a full e-commerce backend to any Payload app\n- Add custom reporting views to Payload's Admin Panel\n- Encrypt specific collections' data\n- Add a full form builder implementation\n- Integrate all `upload`-enabled collections with a third-party file host like S3 or Cloudinary\n- Add custom endpoints or GraphQL queries / mutations with any type of custom functionality that you can think of\n\n## Official Plugins\n\nPayload maintains a set of Official Plugins that solve for some of the common use cases. These plugins are maintained by the Payload team and its contributors and are guaranteed to be stable and up-to-date.\n\n- [Form Builder](./form-builder)\n- [Nested Docs](./nested-docs)\n- [Redirects](./redirects)\n- [Search](./search)\n- [Sentry](./sentry)\n- [SEO](./seo)\n- [Stripe](./stripe)\n\nYou can also [build your own plugin](./build-your-own) to easily extend Payload's functionality in some other way. Once your plugin is ready, consider [sharing it with the community](#community-plugins).\n\nPlugins are changing every day, so be sure to check back often to see what new plugins may have been added. If you have a specific plugin you would like to see, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions).\n\n\u003cBanner type=\"warning\"\u003e\n For a complete list of Official Plugins, visit the [Packages Directory](https://github.com/payloadcms/payload/tree/main/packages) of the [Payload Monorepo](https://github.com/payloadcms/payload).\n\u003c/Banner\u003e\n\n## Community Plugins\n\nCommunity Plugins are those that are maintained entirely by outside contributors. They are a great way to share your work across the ecosystem for others to use. You can discover Community Plugins by browsing the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin).\n\nSome plugins have become so widely used that they are adopted as an [Official Plugin](#official-plugins), such as the [Lexical Plugin](https://github.com/AlessioGr/payload-plugin-lexical). If you have a plugin that you think should be an Official Plugin, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions).\n\n\u003cBanner type=\"warning\"\u003e\n For maintainers building plugins for others to use, please add the `payload-plugin` topic on [GitHub](https://github.com/topics/payload-plugin) to help others find it.\n\u003c/Banner\u003e\n\n## Example\n\nThe base [Payload Config](../configuration/overview) allows for a `plugins` property which takes an `array` of [Plugin Configs](./build-your-own).\n\n```ts\nimport { buildConfig } from 'payload'\nimport { addLastModified } from './addLastModified.ts'\n\nconst config = buildConfig({\n // ...\n // highlight-start\n plugins: [\n addLastModified,\n ],\n // highlight-end\n})\n```\n\n\u003cBanner type=\"warning\"\u003e\n Payload Plugins are executed _after_ the incoming config is validated, but before it is sanitized and has had default options merged in. After all plugins are executed, the full config with all plugins will be sanitized.\n\u003c/Banner\u003e\n\nHere is an example what the `addLastModified` plugin from above might look like. It adds a `lastModifiedBy` field to all Payload collections. For full details, see [how to build your own plugin](./build-your-own).\n\n```ts\nimport { Config, Plugin } from 'payload'\n\nexport const addLastModified: Plugin = (incomingConfig: Config): Config =\u003e {\n // Find all incoming auth-enabled collections\n // so we can create a lastModifiedBy relationship field\n // to all auth collections\n const authEnabledCollections = incomingConfig.collections.filter((collection) =\u003e\n Boolean(collection.auth),\n )\n\n // Spread the existing config\n const config: Config = {\n ...incomingConfig,\n collections: incomingConfig.collections.map((collection) =\u003e {\n // Spread each item that we are modifying,\n // and add our new field - complete with\n // hooks and proper admin UI config\n return {\n ...collection,\n fields: [\n ...collection.fields,\n {\n name: 'lastModifiedBy',\n type: 'relationship',\n relationTo: authEnabledCollections.map(({ slug }) =\u003e slug),\n hooks: {\n beforeChange: [\n ({ req }) =\u003e ({\n value: req?.user?.id,\n relationTo: req?.user?.collection,\n }),\n ],\n },\n admin: {\n position: 'sidebar',\n readOnly: true,\n },\n },\n ],\n }\n }),\n }\n\n return config\n}\n```\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eReminder:\u003c/strong\u003e\n See [how to build your own plugin](./build-your-own) for a more in-depth explication on how create your own Payload Plugin.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"98:T28c7,"])</script><script>self.__next_f.push([1,"\nBuilding your own [Payload Plugin](./overview) is easy, and if you\u0026apos;re already familiar with Payload then you\u0026apos;ll have everything you need to get started. You can either start from scratch or use the [Plugin Template](#plugin-template) to get up and running quickly.\n\n\u003cBanner type=\"success\"\u003e\n To use the template, run `npx create-payload-app@latest -t plugin -n my-new-plugin` directly in\n your terminal or [clone the template directly from\n GitHub](https://github.com/payloadcms/payload-plugin-template).\n\u003c/Banner\u003e\n\nOur plugin template includes everything you need to build a full life-cycle plugin:\n\n- Example files and functions for extending the Payload Config\n- A local dev environment to develop the plugin\n- Test suite with integrated GitHub workflow\n\nBy abstracting your code into a plugin, you\u0026apos;ll be able to reuse your feature across multiple projects and make it available for other developers to use.\n\n## Plugins Recap\n\nHere is a brief recap of how to integrate plugins with Payload, to learn more head back to the [plugin overview page](../plugins/overview).\n\n### How to install a plugin\n\nTo install any plugin, simply add it to your Payload Config in the plugins array.\n\n```\nimport samplePlugin from 'sample-plugin';\n\nconst config = buildConfig({\n plugins: [\n // Add plugins here\n samplePlugin({\n\t\tenabled: true,\n }),\n ],\n});\n\nexport default config;\n```\n\n### Initialization\n\nThe initialization process goes in the following order:\n\n1. Incoming config is validated\n2. Plugins execute\n3. Default options are integrated\n4. Sanitization cleans and validates data\n5. Final config gets initialized\n\n## Plugin Template\n\nIn the [Payload Plugin Template](https://github.com/payloadcms/payload-plugin-template), you will see a common file structure that is used across plugins:\n\n1. `/` root folder - general configuration\n2. `/src` folder - everything related to the plugin\n3. `/dev` folder - sanitized test project for development\n\n### The root folder\n\nIn the root folder, you will see various files related to the configuration of the plugin. We set up our environment in a similar manner in Payload core and across other projects. The only two files you need to modify are:\n\n- **README**.md - This contains instructions on how to use the template. When you are ready, update this to contain instructions on how to use your Plugin.\n- **package**.json - Contains necessary scripts and dependencies. Overwrite the metadata in this file to describe your Plugin.\n\n### The dev folder\n\nThe purpose of the **dev** folder is to provide a sanitized local Payload project. so you can run and test your plugin while you are actively developing it.\n\nDo **not** store any of the plugin functionality in this folder - it is purely an environment to _assist_ you with developing the plugin.\n\nIf you\u0026apos;re starting from scratch, you can easily setup a dev environment like this:\n\n```\nmkdir dev\ncd dev\nnpx create-payload-app@latest\n```\n\nIf you\u0026apos;re using the plugin template, the dev folder is built out for you and the `samplePlugin` has already been installed in `dev/payload.config()`.\n\n```\n plugins: [\n // when you rename the plugin or add options, make sure to update it here\n samplePlugin({\n enabled: false,\n })\n ]\n```\n\nYou can add to the `dev/payload.config` and build out the dev project as needed to test your plugin.\n\nWhen you\u0026apos;re ready to start development, navigate into this folder with `cd dev`\n\nAnd then start the project with `yarn dev` and pull up `http://localhost:3000` in your browser.\n\n## Testing\n\nAnother benefit of the dev folder is that you have the perfect environment established for testing.\n\nA good test suite is essential to ensure quality and stability in your plugin. Payload typically uses [Jest](https://jestjs.io/); a popular testing framework, widely used for testing JavaScript and particularly for applications built with React.\n\nJest organizes tests into test suites and cases. We recommend creating tests based on the expected behavior of your plugin from start to finish. Read more about tests in the [Jest documentation.](https://jestjs.io/)\n\nThe plugin template provides a stubbed out test suite at `dev/plugin.spec.ts` which is ready to go - just add in your own test conditions and you\u0026apos;re all set!\n\n```\nimport payload from 'payload'\n\ndescribe('Plugin tests', () =\u003e {\n // Example test to check for seeded data\n it('seeds data accordingly', async () =\u003e {\n const newCollectionQuery = await payload.find({\n collection: 'newCollection',\n sort: 'createdAt',\n })\n\n newCollection = newCollectionQuery.docs\n\n expect(newCollectionQuery.totalDocs).toEqual(1)\n })\n})\n```\n\n## Seeding data\n\nFor development and testing, you will likely need some data to work with. You can streamline this process by seeding and dropping your database - instead of manually entering data.\n\nIn the plugin template, you can navigate to `dev/src/server.ts` and see an example seed function.\n\n```\nif (process.env.PAYLOAD_SEED === 'true') {\n await seed(payload)\n}\n```\n\nA sample seed function has been created for you at `dev/src/seed`, update this file with additional data as needed.\n\n```\nexport const seed = async (payload: Payload): Promise\u003cvoid\u003e =\u003e {\n payload.logger.info('Seeding data...')\n\n await payload.create({\n collection: 'new-collection',\n data: {\n title: 'Seeded title',\n },\n })\n\n // Add additional seed data here\n}\n\n```\n\n## Building a Plugin\n\nNow that we have our environment setup and dev project ready to go - it\u0026apos;s time to build the plugin!\n\n\n```\nimport type { Config } from 'payload'\n\nexport const samplePlugin =\n (pluginOptions: PluginTypes) =\u003e\n (incomingConfig: Config): Config =\u003e {\n // create copy of incoming config\n let config = { ...incomingConfig }\n\n /**\n * This is where you could modify the\n * config based on the plugin options\n */\n\n // If you wanted to add a new collection:\n config.collections = [\n ...(config.collections || []),\n newCollection,\n ]\n\n // If you wanted to add a new global:\n config.globals = [\n ...(config.globals || []),\n newGlobal,\n ]\n\n /**\n * If you wanted to add a new field to a collection:\n *\n * 1. Loop over collections\n * 2. Find the collection you want to add the field to\n * 3. Add the field to the collection\n */\n\n // If you wanted to add to the onInit:\n config.onInit = async payload =\u003e {\n if (incomingConfig.onInit) await incomingConfig.onInit(payload)\n // Add additional onInit code here\n }\n\n // Finally, return the modified config\n return config\n }\n```\n\nTo reiterate, the essence of a [Payload Plugin](./overview) is simply to extend the [Payload Config](../configuration/overview) - and that is exactly what we are doing in this file.\n\n\n### Spread syntax\n\n[Spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) (or the spread operator) is a feature in JavaScript that uses the dot notation **(...)** to spread elements from arrays, strings, or objects into various contexts.\n\nWe are going to use spread syntax to allow us to add data to existing arrays without losing the existing data. It is crucial to spread the existing data correctly, else this can cause adverse behavior and conflicts with Payload Config and other plugins.\n\nLet\u0026apos;s say you want to build a plugin that adds a new collection:\n\n```\nconfig.collections = [\n ...(config.collections || []),\n newCollection,\n // Add additional collections here\n]\n```\n\nFirst, you need to spread the `config.collections` to ensure that we don\u0026apos;t lose the existing collections. Then you can add any additional collections, just as you would in a regular Payload Config.\n\nThis same logic is applied to other array and object like properties such as admin, globals and hooks:\n\n```\nconfig.globals = [\n ...(config.globals || []),\n // Add additional globals here\n]\n\nconfig.hooks = {\n ...(config.hooks || {}),\n // Add additional hooks here\n}\n```\n\n### Extending functions\nFunction properties cannot use spread syntax. The way to extend them is to execute the existing function if it exists and then run your additional functionality. \n\nHere is an example extending the `onInit` property:\n\n```\nconfig.onInit = async payload =\u003e {\n if (incomingConfig.onInit) await incomingConfig.onInit(payload)\n\n // Add additional onInit code by using the onInitExtension function\n onInitExtension(pluginOptions, payload)\n}\n```\n\n## Types\n\nIf your plugin has options, you should define and provide types for these options in a separate file which gets exported from the main `index.ts`.\n\n```\nexport interface PluginTypes {\n /**\n * Enable or disable plugin\n * @default false\n */\n enabled?: boolean\n}\n\n```\n\nIf possible, include [JSDoc comments](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#types-1) to describe the options and their types. This allows a developer to see details about the options in their editor.\n\n## Best practices\n\nIn addition to the setup covered above, here are other best practices to follow:\n\n### Providing an enable / disable option\n\nFor a better user experience, provide a way to disable the plugin without uninstalling it.\n\n### Include tests in your GitHub CI workflow\n\nIf you\u0026apos;ve configured tests for your package, integrate them into your workflow to run the tests each time you commit to the plugin repository. Learn more about [how to configure tests into your GitHub CI workflow.](https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs)\n\n### Publish your finished plugin to NPM\n\nThe best way to share and allow others to use your plugin once it is complete is to publish an NPM package. This process is straightforward and well documented, find out more about [creating and publishing a NPM package here](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).\n\n### Add payload-plugin topic tag\n\nApply the tag **payload-plugin** to your GitHub repository. This will boost the visibility of your plugin and ensure it gets listed with [existing Payload plugins](https://github.com/topics/payload-plugin).\n\n### Use Semantic Versioning (SemVer)\n\nWith the [Semantic Versioning](https://semver.org/) (SemVer) system you release version numbers that reflect the nature of changes (major, minor, patch). Ensure all major versions reference their Payload compatibility.\n"])</script><script>self.__next_f.push([1,"99:T2866,"])</script><script>self.__next_f.push([1,"\n[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-seo)](https://www.npmjs.com/package/@payloadcms/plugin-seo)\n\nThis plugin allows you to easily manage SEO metadata for your application from within your [Admin Panel](../admin/overview). When enabled on your [Collections](../configuration/collections) and [Globals](../configuration/globals), it adds a new `meta` field group containing `title`, `description`, and `image` by default. Your front-end application can then use this data to render meta tags however your application requires. For example, you would inject a `title` tag into the `\u003chead\u003e` of your page using `meta.title` as its content.\n\nAs users are editing documents within the Admin Panel, they have the option to \"auto-generate\" these fields. When clicked, this plugin will execute your own custom functions that re-generate the title, description, and image. This way you can build your own SEO writing assistance directly into your application. For example, you could append your site name onto the page title, or use the document's excerpt field as the description, or even integrate with some third-party API to generate the image using AI.\n\nTo help you visualize what your page might look like in a search engine, a preview is rendered on page just beneath the meta fields. This preview is updated in real-time as you edit your metadata. There are also visual indicators to help you write effective meta, such as a character counter for the title and description fields. You can even inject your own custom fields into the `meta` field group as your application requires, like `og:title` or `json-ld`. If you've ever used something like Yoast SEO, this plugin might feel very familiar.\n\n\u003cBanner type=\"info\"\u003e\n This plugin is completely open-source and the [source code can be found\n here](https://github.com/payloadcms/payload/tree/main/packages/plugin-seo). If you need help,\n check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a\n bug, please [open a new\n issue](https://github.com/payloadcms/payload/issues/new?assignees=\u0026labels=plugin%3A%20seo\u0026template=bug_report.md\u0026title=plugin-seo%3A)\n with as much detail as possible.\n\u003c/Banner\u003e\n\n## Core features\n\n- Adds a `meta` field group to every SEO-enabled collection or global\n- Allows you to define custom functions to auto-generate metadata\n- Displays hints and indicators to help content editor write effective meta\n- Renders a snippet of what a search engine might display\n- Extendable so you can define custom fields like `og:title` or `json-ld`\n- Soon will support dynamic variable injection\n\n## Installation\n\nInstall the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):\n\n```bash\n pnpm add @payloadcms/plugin-seo\n```\n\n## Basic Usage\n\nIn the `plugins` array of your [Payload Config](../configuration/overview), call the plugin with [options](#options):\n\n```ts\nimport { buildConfig } from 'payload';\nimport { seoPlugin } from '@payloadcms/plugin-seo';\n\nconst config = buildConfig({\n collections: [\n {\n slug: 'pages',\n fields: []\n },\n {\n slug: 'media',\n upload: {\n staticDir: // path to your static directory,\n },\n fields: []\n }\n ],\n plugins: [\n seoPlugin({\n collections: [\n 'pages',\n ],\n uploadsCollection: 'media',\n generateTitle: ({ doc }) =\u003e `Website.com — ${doc.title}`,\n generateDescription: ({ doc }) =\u003e doc.excerpt\n })\n ]\n});\n\nexport default config;\n```\n\n### Options\n\n##### `collections`\n\nAn array of collections slugs to enable SEO. Enabled collections receive a `meta` field which is an object of title, description, and image subfields.\n\n##### `globals`\n\nAn array of global slugs to enable SEO. Enabled globals receive a `meta` field which is an object of title, description, and image subfields.\n\n##### `fields`\n\nA function that takes in the default fields via an object and expects an array of fields in return. You can use this to modify existing fields or add new ones.\n\n```ts\n// payload.config.ts\n{\n // ...\n seoPlugin({\n fields: ({ defaultFields }) =\u003e [\n ...defaultFields,\n {\n name: 'customField',\n type: 'text',\n }\n ]\n })\n}\n```\n\n##### `uploadsCollection`\n\nSet the `uploadsCollection` to your application's upload-enabled collection slug. This is used to provide an `image` field on the `meta` field group.\n\n##### `tabbedUI`\n\nWhen the `tabbedUI` property is `true`, it appends an `SEO` tab onto your config using Payload's [Tabs Field](../fields/tabs). If your collection is not already tab-enabled, meaning the first field in your config is not of type `tabs`, then one will be created for you called `Content`. Defaults to `false`.\n\n\u003cBanner type=\"info\"\u003e\n If you wish to continue to use top-level or sidebar fields with `tabbedUI`, you must not let the\n default `Content` tab get created for you (see the note above). Instead, you must define the first\n field of your config with type `tabs` and place all other fields adjacent to this one.\n\u003c/Banner\u003e\n\n##### `generateTitle`\n\nA function that allows you to return any meta title, including from document's content.\n\n```ts\n// payload.config.ts\n{\n // ...\n seoPlugin({\n generateTitle: ({ doc }) =\u003e `Website.com — ${doc?.title}`,\n })\n}\n```\n\nAll \"generate\" functions receive the following arguments:\n\n| Argument | Description |\n| --- | --- |\n| **`collectionConfig`** | The configuration of the collection. |\n| **`collectionSlug`** | The slug of the collection. |\n| **`doc`** | The data of the current document. |\n| **`docPermissions`** | The permissions of the document. |\n| **`globalConfig`** | The configuration of the global. |\n| **`globalSlug`** | The slug of the global. |\n| **`hasPublishPermission`** | Whether the user has permission to publish the document. |\n| **`hasSavePermission`** | Whether the user has permission to save the document. |\n| **`id`** | The ID of the document. |\n| **`initialData`** | The initial data of the document. |\n| **`initialState`** | The initial state of the document. |\n| **`locale`** | The locale of the document. |\n| **`preferencesKey`** | The preferences key of the document. |\n| **`publishedDoc`** | The published document. |\n| **`req`** | The Payload request object containing `user`, `payload`, `i18n`, etc. |\n| **`title`** | The title of the document. |\n| **`versionsCount`** | The number of versions of the document. |\n\n##### `generateDescription`\n\nA function that allows you to return any meta description, including from document's content.\n\n```ts\n// payload.config.ts\n{\n // ...\n seoPlugin({\n generateDescription: ({ doc }) =\u003e doc?.excerpt,\n })\n}\n```\n\nFor a full list of arguments, see the [`generateTitle`](#generatetitle) function.\n\n##### `generateImage`\n\nA function that allows you to return any meta image, including from document's content.\n\n```ts\n// payload.config.ts\n{\n // ...\n seoPlugin({\n generateImage: ({ doc }) =\u003e doc?.featuredImage,\n })\n}\n```\n\nFor a full list of arguments, see the [`generateTitle`](#generatetitle) function.\n\n##### `generateURL`\n\nA function called by the search preview component to display the actual URL of your page.\n\n```ts\n// payload.config.ts\n{\n // ...\n seoPlugin({\n generateURL: ({ doc, collectionSlug }) =\u003e\n `https://yoursite.com/${collectionSlug}/${doc?.slug}`,\n })\n}\n```\n\nFor a full list of arguments, see the [`generateTitle`](#generatetitle) function.\n\n#### `interfaceName`\n\nRename the meta group interface name that is generated for TypeScript and GraphQL.\n\n```ts\n// payload.config.ts\n{\n // ...\n seoPlugin({\n interfaceName: 'customInterfaceNameSEO',\n })\n}\n```\n\n## Direct use of fields\n\nThere is the option to directly import any of the fields from the plugin so that you can include them anywhere as needed.\n\n\u003cBanner type=\"info\"\u003e\n You will still need to configure the plugin in the Payload Config in order to configure the generation functions.\n Since these fields are imported and used directly, they don't have access to the plugin config so they may need additional arguments to work the same way.\n\u003c/Banner\u003e\n\n```ts\nimport { MetaDescriptionField, MetaImageField, MetaTitleField, OverviewField, PreviewField } from '@payloadcms/plugin-seo/fields'\n\n// Used as fields\nMetaImageField({\n // the upload collection slug\n relationTo: 'media',\n\n // if the `generateImage` function is configured\n hasGenerateFn: true,\n})\n\nMetaDescriptionField({\n // if the `generateDescription` function is configured\n hasGenerateFn: true,\n})\n\nMetaTitleField({\n // if the `generateTitle` function is configured\n hasGenerateFn: true,\n})\n\nPreviewField({\n // if the `generateUrl` function is configured\n hasGenerateFn: true,\n\n // field paths to match the target field for data\n titlePath: 'meta.title',\n descriptionPath: 'meta.description',\n})\n\nOverviewField({\n // field paths to match the target field for data\n titlePath: 'meta.title',\n descriptionPath: 'meta.description',\n imagePath: 'meta.image',\n})\n```\n\n\u003cBanner type=\"info\"\u003e\nTip: You can override the length rules by changing the minLength and maxLength props on the fields. In the case of the OverviewField you can use `titleOverrides` and `descriptionOverrides` to override the length rules.\n\u003c/Banner\u003e\n\n## TypeScript\n\nAll types can be directly imported:\n\n```ts\nimport {\n PluginConfig,\n GenerateTitle,\n GenerateDescription\n GenerateURL\n} from '@payloadcms/plugin-seo/types';\n```\n\nYou can then pass the collections from your generated Payload types into the generation types, for example:\n\n```ts\nimport { Page } from './payload-types.ts';\n\nimport { GenerateTitle } from '@payloadcms/plugin-seo/types';\n\nconst generateTitle: GenerateTitle\u003cPage\u003e = async ({ doc, locale }) =\u003e {\n return `Website.com — ${doc?.title}`\n}\n```\n\n## Examples\n\nThe [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommere) which demonstrates exactly how to configure this plugin in Payload and implement it on your front-end.\n\n## Screenshots\n\n![image](https://user-images.githubusercontent.com/70709113/163850633-f3da5f8e-2527-4688-bc79-17233307a883.png)\n"])</script><script>self.__next_f.push([1,"9a:T5410,"])</script><script>self.__next_f.push([1,"\n[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-form-builder)](https://www.npmjs.com/package/@payloadcms/plugin-form-builder)\n\nThis plugin allows you to build and manage custom forms directly within the [Admin Panel](../admin/overview). Instead of hard-coding a new form into your website or application every time you need one, admins can simply define the schema for each form they need on-the-fly, and your front-end can map over this schema, render its own UI components, and match your brand's design system.\n\nAll form submissions are stored directly in your database and are managed directly from the Admin Panel. When forms are submitted, you can display a custom on-screen confirmation message to the user or redirect them to a dedicated confirmation page. You can even send dynamic, personalized emails derived from the form's data. For example, you may want to send a confirmation email to the user who submitted the form, and also send a notification email to your team.\n\nForms can be as simple or complex as you need, from a basic contact form, to a multi-step lead generation engine, or even a donation form that processes payment. You may not need to reach for third-party services like HubSpot or Mailchimp for this, but instead use your own first-party tooling, built directly into your own application.\n\n\u003cBanner type=\"info\"\u003e\n This plugin is completely open-source and the [source code can be found\n here](https://github.com/payloadcms/payload/tree/main/packages/plugin-form-builder). If you need\n help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've\n found a bug, please [open a new\n issue](https://github.com/payloadcms/payload/issues/new?assignees=\u0026labels=plugin%3A%20form-builder\u0026template=bug_report.md\u0026title=plugin-form-builder%3A)\n with as much detail as possible.\n\u003c/Banner\u003e\n\n## Core Features\n\n- Build completely dynamic forms directly from the Admin Panel for a variety of use cases\n- Render forms on your front-end using your own UI components and match your brand's design system\n- Send dynamic, personalized emails upon form submission to multiple recipients, derived from the form's data\n- Display a custom confirmation message or automatically redirect upon form submission\n- Build dynamic prices based on form input to use for payment processing (optional)\n\n## Installation\n\nInstall the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):\n\n```bash\npnpm add @payloadcms/plugin-form-builder\n```\n\n## Basic Usage\n\nIn the `plugins` array of your [Payload Config](../configuration/overview), call the plugin with [options](#options):\n\n```ts\nimport { buildConfig } from 'payload'\nimport { formBuilderPlugin } from '@payloadcms/plugin-form-builder'\n\nconst config = buildConfig({\n collections: [\n {\n slug: 'pages',\n fields: [],\n },\n ],\n plugins: [\n formBuilderPlugin({\n // see below for a list of available options\n }),\n ],\n})\n\nexport default config\n```\n\n## Options\n\n### `fields` (option)\n\nThe `fields` property is an object of field types to allow your admin editors to build forms with. To override default settings, pass either a boolean value or a partial [Payload Block](../fields/blocks#block-configs) _keyed to the block's slug_. See [Fields](#fields) for more details.\n\n```ts\n// payload.config.ts\nformBuilderPlugin({\n // ...\n fields: {\n text: true,\n textarea: true,\n select: true,\n email: true,\n state: true,\n country: true,\n checkbox: true,\n number: true,\n message: true,\n payment: false,\n },\n})\n```\n\n### `redirectRelationships`\n\nThe `redirectRelationships` property is an array of collection slugs that, when enabled, are populated as options in the form's `redirect` field. This field is used to redirect the user to a dedicated confirmation page upon form submission (optional).\n\n```ts\n// payload.config.ts\nformBuilderPlugin({\n // ...\n redirectRelationships: ['pages'],\n})\n```\n\n### `beforeEmail`\n\nThe `beforeEmail` property is a [beforeChange](../hooks/globals#beforechange) hook that is called just after emails are prepared, but before they are sent. This is a great place to inject your own HTML template to add custom styles.\n\n```ts\n// payload.config.ts\nformBuilderPlugin({\n // ...\n beforeEmail: (emailsToSend, beforeChangeParams) =\u003e {\n // modify the emails in any way before they are sent\n return emails.map((email) =\u003e ({\n ...email,\n html: email.html, // transform the html in any way you'd like (maybe wrap it in an html template?)\n }))\n },\n})\n```\n\nFor full types with `beforeChangeParams`, you can import the types from the plugin:\n\n```ts\nimport type { BeforeEmail } from '@payloadcms/plugin-form-builder'\n// Your generated FormSubmission type\nimport type {FormSubmission} from '@payload-types'\n\n// Pass it through and 'data' or 'originalDoc' will now be typed\nconst beforeEmail: BeforeEmail\u003cFormSubmission\u003e = (emailsToSend, beforeChangeParams) =\u003e {\n // modify the emails in any way before they are sent\n return emails.map((email) =\u003e ({\n ...email,\n html: email.html, // transform the html in any way you'd like (maybe wrap it in an html template?)\n }))\n}\n```\n\n### `defaultToEmail`\n\nProvide a fallback for the email address to send form submissions to. If the email in form configuration does not have a to email set, this email address will be used. If this is not provided then it falls back to the `defaultFromAddress` in your [email configuration](../email/overview).\n\n```ts\n// payload.config.ts\nformBuilderPlugin({\n // ...\n defaultToEmail: 'test@example.com',\n})\n```\n\n### `formOverrides`\n\nOverride anything on the `forms` collection by sending a [Payload Collection Config](../configuration/collections) to the `formOverrides` property.\n\nNote that the `fields` property is a function that receives the default fields and returns an array of fields. This is because the `fields` property is a special case that is merged with the default fields, rather than replacing them. This allows you to map over default fields and modify them as needed.\n\n\u003cBanner type=\"warning\"\u003e\nGood to know: The form collection is publicly available to read by default. The emails field is locked for authenticated users only. If you have any frontend users you should override the access permissions for both the collection and the emails field to make sure you don't leak out any private emails.\n\u003c/Banner\u003e\n\n```ts\n// payload.config.ts\nformBuilderPlugin({\n // ...\n formOverrides: {\n slug: 'contact-forms',\n access: {\n read: ({ req: { user } }) =\u003e !!user, // authenticated users only\n update: () =\u003e false,\n },\n fields: ({ defaultFields }) =\u003e {\n return [\n ...defaultFields,\n {\n name: 'custom',\n type: 'text',\n },\n ]\n },\n },\n})\n```\n\n### `formSubmissionOverrides`\n\nOverride anything on the `form-submissions` collection by sending a [Payload Collection Config](../configuration/collections) to the `formSubmissionOverrides` property.\n\n\u003cBanner type=\"warning\"\u003e\n By default, this plugin relies on [Payload access\n control](../access-control/collections) to restrict the `update` and\n `read` operations on the `form-submissions` collection. This is because _anyone_ should be able to\n create a form submission, even from a public-facing website, but _no one_ should be able to update\n a submission once it has been created, or read a submission unless they have permission. You can\n override this behavior or any other property as needed.\n\u003c/Banner\u003e\n\n```ts\n// payload.config.ts\nformBuilderPlugin({\n // ...\n formSubmissionOverrides: {\n slug: 'leads',\n fields: ({ defaultFields }) =\u003e {\n return [\n ...defaultFields,\n {\n name: 'custom',\n type: 'text',\n },\n ]\n },\n },\n})\n```\n\n### `handlePayment`\n\nThe `handlePayment` property is a [beforeChange](../hooks/globals#beforechange) hook that is called upon form submission. You can integrate into any third-party payment processing API here to accept payment based on form input. You can use the `getPaymentTotal` function to calculate the total cost after all conditions have been applied. This is only applicable if the form has enabled the `payment` field.\n\nFirst import the utility function. This will execute all of the price conditions that you have set in your form's `payment` field and returns the total price.\n\n```ts\n// payload.config.ts\nimport { getPaymentTotal } from '@payloadcms/plugin-form-builder'\n```\n\nThen in your plugin's config:\n\n```ts\n// payload.config.ts\nformBuilderPlugin({\n // ...\n handlePayment: async ({ form, submissionData }) =\u003e {\n // first calculate the price\n const paymentField = form.fields?.find((field) =\u003e field.blockType === 'payment')\n const price = getPaymentTotal({\n basePrice: paymentField.basePrice,\n priceConditions: paymentField.priceConditions,\n fieldValues: submissionData,\n })\n // then asynchronously process the payment here\n },\n})\n```\n\n## Fields\n\nEach field represents a form input. To override default settings pass either a boolean value or a partial [Payload Block](../fields/blocks) _keyed to the block's slug_. See [Field Overrides](#field-overrides) for more details on how to do this.\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \"Fields\" here is in reference to the _fields to build forms with_, not to be confused with the _fields\n of a collection_ which are set via `formOverrides.fields`.\n\u003c/Banner\u003e\n\n### Text\n\nMaps to a `text` input in your front-end. Used to collect a simple string.\n\n| Property | Type | Description |\n| -------------- | -------- | ---------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | string | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n\n### Textarea\n\nMaps to a `textarea` input on your front-end. Used to collect a multi-line string.\n\n| Property | Type | Description |\n| -------------- | -------- | ---------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | string | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n\n### Select\n\nMaps to a `select` input on your front-end. Used to display a list of options.\n\n| Property | Type | Description |\n| -------------- | -------- | -------------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | string | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n| `options` | array | An array of objects with `label` and `value` properties. |\n\n### Email (field)\n\nMaps to a `text` input with type `email` on your front-end. Used to collect an email address.\n\n| Property | Type | Description |\n| -------------- | -------- | ---------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | string | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n\n### State\n\nMaps to a `select` input on your front-end. Used to collect a US state.\n\n| Property | Type | Description |\n| -------------- | -------- | ---------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | string | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n\n### Country\n\nMaps to a `select` input on your front-end. Used to collect a country.\n\n| Property | Type | Description |\n| -------------- | -------- | ---------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | string | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n\n### Checkbox\n\nMaps to a `checkbox` input on your front-end. Used to collect a boolean value.\n\n| Property | Type | Description |\n| -------------- | -------- | ---------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | checkbox | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n\n### Number\n\nMaps to a `number` input on your front-end. Used to collect a number.\n\n| Property | Type | Description |\n| -------------- | -------- | ---------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | number | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n\n### Message\n\nMaps to a `RichText` component on your front-end. Used to display an arbitrary message to the user anywhere in the form.\n\n| property | type | description |\n| --------- | -------- | ----------------------------------- |\n| `message` | richText | The message to display on the form. |\n\n### Payment\n\nAdd this field to your form if it should collect payment. Upon submission, the `handlePayment` callback is executed with the form and submission data. You can use this to integrate with any third-party payment processing API.\n\n| property | type | description |\n| ----------------- | -------- | --------------------------------------------------------------------------------- |\n| `name` | string | The name of the field. |\n| `label` | string | The label of the field. |\n| `defaultValue` | number | The default value of the field. |\n| `width` | string | The width of the field on the front-end. |\n| `required` | checkbox | Whether or not the field is required when submitted. |\n| `priceConditions` | array | An array of objects that define the price conditions. See below for more details. |\n\n#### Price Conditions\n\nEach of the `priceConditions` are executed by the `getPaymentTotal` utility that this plugin provides. You can call this function in your `handlePayment` callback to dynamically calculate the total price of a form upon submission based on the user's input. For example, you could create a price condition that says \"if the user selects 'yes' for this checkbox, add $10 to the total price\".\n\n| property | type | description |\n| ------------------ | ------------ | ------------------------------------------------ |\n| `fieldToUse` | relationship | The field to use to determine the price. |\n| `condition` | string | The condition to use to determine the price. |\n| `valueForOperator` | string | The value to use for the operator. |\n| `operator` | string | The operator to use to determine the price. |\n| `valueType` | string | The type of value to use to determine the price. |\n| `value` | string | The value to use to determine the price. |\n\n### Field Overrides\n\nYou can provide your own custom fields by passing a new [Payload Block](../fields/blocks#block-configs) object into `fields`. You can override or extend any existing fields by first importing the `fields` from the plugin:\n\n```ts\nimport { fields } from '@payloadcms/plugin-form-builder'\n```\n\nThen merging it into your own custom field:\n\n```ts\n// payload.config.ts\nformBuilderPlugin({\n // ...\n fields: {\n text: {\n ...fields.text,\n labels: {\n singular: 'Custom Text Field',\n plural: 'Custom Text Fields',\n },\n },\n },\n})\n```\n\n## Email\n\nThis plugin relies on the [email configuration](../email/overview) defined in your payload configuration. It will read from your config and attempt to send your emails using the credentials provided.\n\n### Email formatting\n\nThe email contents supports rich text which will be serialized to HTML on the server before being sent. By default it reads the global configuration of your rich text editor.\n\nThe email subject and body supports inserting dynamic fields from the form submission data using the `{{field_name}}` syntax. For example, if you have a field called `name` in your form, you can include this in the email body like so:\n\n```html\nThank you for your submission, {{name}}!\n```\n\nYou can also use `{{*}}` as a wildcard to output all the data in a key:value format and `{{*:table}}` to output all the data in a table format.\n\n## TypeScript\n\nAll types can be directly imported:\n\n```ts\nimport type {\n PluginConfig,\n Form,\n FormSubmission,\n FieldsConfig,\n BeforeEmail,\n HandlePayment,\n ...\n} from \"@payloadcms/plugin-form-builder/types\";\n```\n\n## Examples\n\nThe [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) contains an official [Form Builder Plugin Example](https://github.com/payloadcms/payload/tree/main/examples/form-builder) which demonstrates exactly how to configure this plugin in Payload and implement it on your front-end. We've also included an in-depth walk-through of how to build a form from scratch in our [Form Builder Plugin Blog Post](https://payloadcms.com/blog/create-custom-forms-with-the-official-form-builder-plugin).\n\n## Troubleshooting\n\nBelow are some common troubleshooting tips. To help other developers, please contribute to this section as you troubleshoot your own application.\n\n#### SendGrid 403 Forbidden Error\n\n- If you are using [SendGrid Link Branding](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-link-branding) to remove the \"via sendgrid.net\" part of your email, you must also setup [Domain Authentication](https://docs.sendgrid.com/ui/account-and-settings/how-to-set-up-domain-authentication). This means you can only send emails from an address on this domain — so the `from` addresses in your form submission emails **_cannot_** be anything other than `something@your_domain.com`. This means that from `{{email}}` will not work, but `website@your_domain.com` will. You can still send the form's email address in the body of the email.\n\n## Screenshots\n\n![screenshot 1](https://github.com/payloadcms/plugin-form-builder/blob/main/images/screenshot-1.jpg?raw=true)\n\n\u003cbr /\u003e\n![screenshot 2](https://github.com/payloadcms/plugin-form-builder/blob/main/images/screenshot-2.jpg?raw=true)\n\u003cbr /\u003e\n![screenshot 3](https://github.com/payloadcms/plugin-form-builder/blob/main/images/screenshot-3.jpg?raw=true)\n\u003cbr /\u003e\n![screenshot 4](https://github.com/payloadcms/plugin-form-builder/blob/main/images/screenshot-4.jpg?raw=true)\n\u003cbr /\u003e\n![screenshot 5](https://github.com/payloadcms/plugin-form-builder/blob/main/images/screenshot-5.jpg?raw=true)\n\u003cbr /\u003e\n![screenshot 6](https://github.com/payloadcms/plugin-form-builder/blob/main/images/screenshot-6.jpg?raw=true)\n"])</script><script>self.__next_f.push([1,"9b:T2703,"])</script><script>self.__next_f.push([1,"\n[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-nested-docs)](https://www.npmjs.com/package/@payloadcms/plugin-nested-docs)\n\nThis plugin allows you to easily nest the documents of your application inside of one another. It does so by adding a\nnew `parent` field onto each of your documents that, when selected, attaches itself to the parent's tree. When you edit\nthe great-great-grandparent of a document, for instance, all of its descendants are recursively updated. This is an\nextremely powerful way of achieving hierarchy within a collection, such as parent/child relationship between pages.\n\nDocuments also receive a new `breadcrumbs` field. Once a parent is assigned, these breadcrumbs are populated based on\neach ancestor up the tree. Breadcrumbs allow you to dynamically generate labels and URLs based on the document's\nposition in the hierarchy. Even if the slug of a parent document changes, or the entire tree is nested another level\ndeep, changes will cascade down the entire tree and all breadcrumbs will reflect those changes.\n\nWith this pattern you can perform whatever side-effects your applications needs on even the most deeply nested\ndocuments. For example, you could easily add a custom `fullTitle` field onto each document and inject the parent's title\nonto it, such as \"Parent Title \u003e Child Title\". This would allow you to then perform searches and filters based on _that_\nfield instead of the original title. This is especially useful if you happen to have two documents with identical titles\nbut different parents.\n\n\u003cBanner type=\"info\"\u003e\n This plugin is completely open-source and the [source code can be found\n here](https://github.com/payloadcms/payload/tree/main/packages/plugin-nested-docs). If you need\n help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've\n found a bug, please [open a new\n issue](https://github.com/payloadcms/payload/issues/new?assignees=\u0026labels=plugin%3A%20nested-docs\u0026template=bug_report.md\u0026title=plugin-nested-docs%3A)\n with as much detail as possible.\n\u003c/Banner\u003e\n\n## Core features\n\n- Automatically adds a `parent` relationship field to each document\n- Allows for parent/child relationships between documents within the same collection\n- Recursively updates all descendants when a parent is changed\n- Automatically populates a `breadcrumbs` field with all ancestors up the tree\n- Dynamically generate labels and URLs for each breadcrumb\n- Supports localization\n\n## Installation\n\nInstall the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com),\nor [PNPM](https://pnpm.io):\n\n```bash\n pnpm add @payloadcms/plugin-nested-docs\n```\n\n## Basic Usage\n\nIn the `plugins` array of your [Payload Config](../configuration/overview), call the plugin\nwith [options](#options):\n\n```ts\nimport { buildConfig } from 'payload'\nimport { nestedDocsPlugin } from '@payloadcms/plugin-nested-docs'\n\nconst config = buildConfig({\n collections: [\n {\n slug: 'pages',\n fields: [\n {\n name: 'title',\n type: 'text',\n },\n {\n name: 'slug',\n type: 'text',\n },\n ],\n },\n ],\n plugins: [\n nestedDocsPlugin({\n collections: ['pages'],\n generateLabel: (_, doc) =\u003e doc.title,\n generateURL: (docs) =\u003e docs.reduce((url, doc) =\u003e `${url}/${doc.slug}`, ''),\n }),\n ],\n})\n\nexport default config\n```\n\n### Fields\n\n#### Parent\n\nThe `parent` relationship field is automatically added to every document which allows editors to choose another document\nfrom the same collection to act as the direct parent.\n\n#### Breadcrumbs\n\nThe `breadcrumbs` field is an array which dynamically populates all parent relationships of a document up to the top\nlevel and stores the following fields.\n\n| Field | Description |\n| ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `label` | The label of the breadcrumb. This field is automatically set to either the `collection.admin.useAsTitle` (if defined) or is set to the `ID` of the document. You can also dynamically define the `label` by passing a function to the options property of [`generateLabel`](#generatelabel). |\n| `url` | The URL of the breadcrumb. By default, this field is undefined. You can manually define this field by passing a property called function to the plugin options property of [`generateURL`](#generateurl). |\n\n### Options\n\n#### `collections`\n\nAn array of collections slugs to enable nested docs.\n\n#### `generateLabel`\n\nEach `breadcrumb` has a required `label` field. By default, its value will be set to the collection's `admin.useAsTitle`\nor fallback the the `ID` of the document.\n\nYou can also pass a function to dynamically set the `label` of your breadcrumb.\n\n```ts\n// payload.config.ts\nnestedDocsPlugin({\n //...\n generateLabel: (_, doc) =\u003e doc.title, // NOTE: 'title' is a hypothetical field\n})\n```\n\nThe function takes two arguments and returns a string:\n\n| Argument | Type | Description |\n| -------- | -------- | -------------------------------------------- |\n| `docs` | `Array` | An array of the breadcrumbs up to that point |\n| `doc` | `Object` | The current document being edited |\n\n#### `generateURL`\n\nA function that allows you to dynamically generate each breadcrumb `url`. Each `breadcrumb` has an optional `url` field\nwhich is undefined by default. For example, you might want to format a full URL to contain all breadcrumbs up to\nthat point, like `/about-us/company/our-team`.\n\n```ts\n// payload.config.ts\nnestedDocsPlugin({\n //...\n generateURL: (docs) =\u003e docs.reduce((url, doc) =\u003e `${url}/${doc.slug}`, ''), // NOTE: 'slug' is a hypothetical field\n})\n```\n\n| Argument | Type | Description |\n| -------- | -------- | -------------------------------------------- |\n| `docs` | `Array` | An array of the breadcrumbs up to that point |\n| `doc` | `Object` | The current document being edited |\n\n#### `parentFieldSlug`\n\nWhen defined, the `parent` field will not be provided for you automatically, and instead, expects you to add your\nown `parent` field to each collection manually. This gives you complete control over where you put the field in your\nadmin dashboard, etc. Set this property to the `name` of your custom field.\n\n#### `breadcrumbsFieldSlug`\n\nWhen defined, the `breadcrumbs` field will not be provided for you, and instead, expects you to add your\nown `breadcrumbs` field to each collection manually. Set this property to the `name` of your custom field.\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n If you opt out of automatically being provided a `parent` or `breadcrumbs` field, you need to make\n sure that both fields are placed at the top-level of your document. They cannot exist within any\n nested data structures like a `group`, `array`, or `blocks`.\n\u003c/Banner\u003e\n\n## Overrides\n\nYou can also extend the built-in `parent` and `breadcrumbs` fields per collection by using the `createParentField`\nand `createBreadcrumbField` methods. They will merge your customizations overtop the plugin's base field configurations.\n\n```ts\nimport type { CollectionConfig } from 'payload'\nimport { createParentField } from '@payloadcms/plugin-nested-docs/fields'\nimport { createBreadcrumbsField } from '@payloadcms/plugin-nested-docs/fields'\n\nconst examplePageConfig: CollectionConfig = {\n slug: 'pages',\n fields: [\n createParentField(\n // First argument is equal to the slug of the collection\n // that the field references\n 'pages',\n\n // Second argument is equal to field overrides that you specify,\n // which will be merged into the base parent field config\n {\n admin: {\n position: 'sidebar',\n },\n // Note: if you override the `filterOptions` of the `parent` field,\n // be sure to continue to prevent the document from referencing itself as the parent like this:\n // filterOptions: ({ id }) =\u003e ({ id: {not_equals: id }})`\n },\n ),\n createBreadcrumbsField(\n // First argument is equal to the slug of the collection\n // that the field references\n 'pages',\n\n // Argument equal to field overrides that you specify,\n // which will be merged into the base `breadcrumbs` field config\n {\n label: 'Page Breadcrumbs',\n },\n ),\n ],\n}\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n If overriding the `name` of either `breadcrumbs` or `parent` fields, you must specify the\n `breadcrumbsFieldSlug` or `parentFieldSlug` respectively.\n\u003c/Banner\u003e\n\n## Localization\n\nThis plugin supports localization by default. If the `localization` property is set in your Payload Config,\nthe `breadcrumbs` field is automatically localized. For more details on how localization works in Payload, see\nthe [Localization](../configuration/localization) docs.\n\n## TypeScript\n\nAll types can be directly imported:\n\n```ts\nimport { PluginConfig, GenerateURL, GenerateLabel } from '@payloadcms/plugin-nested-docs/types'\n```\n\n## Examples\n\nThe [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this plugin.\n"])</script><script>self.__next_f.push([1,"9c:T10a1,"])</script><script>self.__next_f.push([1,"\n[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-redirects)](https://www.npmjs.com/package/@payloadcms/plugin-redirects)\n\nThis plugin allows you to easily manage redirects for your application from within your [Admin Panel](../admin/overview). It does so by adding a `redirects` collection to your config that allows you specify a redirect from one URL to another. Your front-end application can use this data to automatically redirect users to the correct page using proper HTTP status codes. This is useful for SEO, indexing, and search engine ranking when re-platforming or when changing your URL structure.\n\nFor example, if you have a page at `/about` and you want to change it to `/about-us`, you can create a redirect from the old page to the new one, then you can use this data to write HTTP redirects into your front-end application. This will ensure that users are redirected to the correct page without penalty because search engines are notified of the change at the request level. This is a very lightweight plugin that will allow you to integrate managed redirects for any front-end framework.\n\n\u003cBanner type=\"info\"\u003e\n This plugin is completely open-source and the [source code can be found\n here](https://github.com/payloadcms/payload/tree/main/packages/plugin-redirects). If you need\n help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've\n found a bug, please [open a new\n issue](https://github.com/payloadcms/payload/issues/new?assignees=\u0026labels=plugin%3A%redirects\u0026template=bug_report.md\u0026title=plugin-redirects%3A)\n with as much detail as possible.\n\u003c/Banner\u003e\n\n## Core features\n\n- Adds a `redirects` collection to your config that:\n - includes a `from` and `to` fields\n - allows `to` to be a document reference\n\n## Installation\n\nInstall the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):\n\n```bash\n pnpm add @payloadcms/plugin-redirects\n```\n\n## Basic Usage\n\nIn the `plugins` array of your [Payload Config](../configuration/overview), call the plugin with [options](#options):\n\n```ts\nimport { buildConfig } from 'payload'\nimport { redirectsPlugin } from '@payloadcms/plugin-redirects'\n\nconst config = buildConfig({\n collections: [\n {\n slug: 'pages',\n fields: [],\n },\n ],\n plugins: [\n redirectsPlugin({\n collections: ['pages'],\n }),\n ],\n})\n\nexport default config\n```\n\n### Options\n\n| Option | Type | Description |\n| ------------- | ---------- | ----------------------------------------------------------------------------------------------- |\n| `collections` | `string[]` | An array of collection slugs to populate in the `to` field of each redirect. |\n| `overrides` | `object` | A partial collection config that allows you to override anything on the `redirects` collection. |\n| `redirectTypes` | `string[]` | Provide an array of redirects if you want to provide options for the type of redirects to be supported. |\n| `redirectTypeFieldOverride` | `Field` | A partial Field config that allows you to override the Redirect Type field if enabled above. |\n\nNote that the fields in overrides take a function that receives the default fields and returns an array of fields. This allows you to add fields to the collection.\n\n```ts\nredirectsPlugin({\n collections: ['pages'],\n overrides: {\n fields: ({ defaultFields }) =\u003e {\n return [\n ...defaultFields,\n {\n type: 'text',\n name: 'customField',\n },\n ]\n },\n },\n redirectTypes: ['301', '302'],\n redirectTypeFieldOverride: {\n label: 'Redirect Type (Overridden)',\n },\n})\n```\n\n## TypeScript\n\nAll types can be directly imported:\n\n```ts\nimport { PluginConfig } from '@payloadcms/plugin-redirects/types'\n```\n\n## Examples\n\nThe [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) also contains an official [Website Template](https://github.com/payloadcms/payload/tree/main/templates/website) and [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce), both of which use this plugin.\n"])</script><script>self.__next_f.push([1,"9d:T1b34,"])</script><script>self.__next_f.push([1,"\n[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-search)](https://www.npmjs.com/package/@payloadcms/plugin-search)\n\nThis plugin generates records of your documents that are extremely fast to search on. It does so by creating a new `search` collection that is indexed in the database then saving a static copy of each of your documents using only search-critical data. Search records are automatically created, synced, and deleted behind-the-scenes as you manage your application's documents.\n\nFor example, if you have a posts collection that is extremely large and complex, this would allow you to sync just the title, excerpt, and slug of each post so you can query on _that_ instead of the original post directly. Search records are static, so querying them also has the significant advantage of bypassing any hooks that may present be on the original documents. You define exactly what data is synced, and you can even modify or fallback this data before it is saved on a per-document basis.\n\nTo query search results, use all the existing Payload APIs that you are already familiar with. You can also prioritize search results by setting a custom priority for each collection. For example, you may want to list blog posts before pages. Or you may want one specific post to always take appear first. Search records are given a `priority` field that can be used as the `?sort=` parameter in your queries.\n\nThis plugin is a great way to implement a fast, immersive search experience such as a search bar in a front-end application. Many applications may not need the power and complexity of a third-party service like Algolia or ElasticSearch. This plugin provides a first-party alternative that is easy to set up and runs entirely on your own database.\n\n\u003cBanner type=\"info\"\u003e\n This plugin is completely open-source and the [source code can be found\n here](https://github.com/payloadcms/payload/tree/main/packages/plugin-search). If you need help,\n check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a\n bug, please [open a new\n issue](https://github.com/payloadcms/payload/issues/new?assignees=\u0026labels=plugin%3A%20search\u0026template=bug_report.md\u0026title=plugin-search%3A)\n with as much detail as possible.\n\u003c/Banner\u003e\n\n## Core Features\n\n- Automatically adds an indexed `search` collection to your database\n- Automatically creates, syncs, and deletes search records as you manage your documents\n- Saves only search-critical data that you define (e.g. title, excerpt, etc.)\n- Allows you to query search results using first-party Payload APIs\n- Allows you to query documents without triggering any of their underlying hooks\n- Allows you to easily prioritize search results by collection or document\n\n## Installation\n\nInstall the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):\n\n```bash\n pnpm add @payloadcms/plugin-search\n```\n\n## Basic Usage\n\nIn the `plugins` array of your [Payload Config](../configuration/overview), call the plugin with [options](#options):\n\n```js\nimport { buildConfig } from 'payload'\nimport { searchPlugin } from '@payloadcms/plugin-search'\n\nconst config = buildConfig({\n collections: [\n {\n slug: 'pages',\n fields: [],\n },\n {\n slug: 'posts',\n fields: [],\n },\n ],\n plugins: [\n searchPlugin({\n collections: ['pages', 'posts'],\n defaultPriorities: {\n pages: 10,\n posts: 20,\n },\n }),\n ],\n})\n\nexport default config\n```\n\n### Options\n\n#### `collections`\n\nThe `collections` property is an array of collection slugs to enable syncing to search. Enabled collections receive a `beforeChange` and `afterDelete` hook that creates, updates, and deletes its respective search record as it changes over time.\n\n### `localize`\n\nBy default, the search plugin will add `localization: true` to the `title` field of the newly added `search` collection if you have localization enabled. If you would like to disable this behavior, you can set this to `false`.\n\n#### `defaultPriorities`\n\nThis plugin automatically adds a `priority` field to the `search` collection that can be used as the `?sort=` parameter in your queries. For example, you may want to list blog posts before pages. Or you may want one specific post to always take appear first.\n\nThe `defaultPriorities` property is used to set a fallback `priority` on search records during the `create` operation. It accepts an object with keys that are your collection slugs and values that can either be a number or a function that returns a number. The function receives the `doc` as an argument, which is the document being created.\n\n```ts\n// payload.config.ts\n{\n // ...\n searchPlugin({\n defaultPriorities: {\n pages: ({ doc }) =\u003e (doc.title.startsWith('Hello, world!') ? 1 : 10),\n posts: 20,\n },\n }),\n}\n```\n\n#### `searchOverrides`\n\nThis plugin automatically creates the `search` collection, but you can override anything on this collection via the `searchOverrides` property. It accepts anything from the [Payload Collection Config](../configuration/collections) and merges it in with the default `search` collection config provided by the plugin.\n\nNote that the `fields` property is a function that receives an object with a `defaultFields` key. This is an array of fields that are automatically added to the `search` collection. You can modify this array or add new fields to it.\n\n```ts\n// payload.config.ts\n{\n // ...\n searchPlugin({\n searchOverrides: {\n slug: 'search-results',\n fields: ({ defaultFields }) =\u003e [\n ...defaultFields,\n {\n name: 'excerpt',\n type: 'textarea',\n admin: {\n position: 'sidebar',\n },\n },\n ],\n },\n }),\n}\n```\n\n#### `beforeSync`\n\nBefore creating or updating a search record, the `beforeSync` function runs. This is an [afterChange](../hooks/globals#afterchange) hook that allows you to modify the data or provide fallbacks before its search record is created or updated.\n\n```ts\n// payload.config.ts\n{\n // ...\n searchPlugin({\n beforeSync: ({ originalDoc, searchDoc }) =\u003e ({\n ...searchDoc,\n // - Modify your docs in any way here, this can be async\n // - You also need to add the `excerpt` field in the `searchOverrides` config\n excerpt: originalDoc?.excerpt || 'This is a fallback excerpt',\n }),\n }),\n}\n```\n\n#### `syncDrafts`\n\nWhen `syncDrafts` is true, draft documents will be synced to search. This is false by default. You must have [Payload Drafts](../versions/drafts) enabled for this to apply.\n\n#### `deleteDrafts`\n\nIf true, will delete documents from search whose status changes to draft. This is true by default. You must have [Payload Drafts](../versions/drafts) enabled for this to apply.\n\n## TypeScript\n\nAll types can be directly imported:\n\n```ts\nimport type { SearchConfig, BeforeSync } from '@payloadcms/plugin-search/types'\n```\n"])</script><script>self.__next_f.push([1,"9e:T1235,"])</script><script>self.__next_f.push([1,"\n[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-sentry)](https://www.npmjs.com/package/@payloadcms/plugin-sentry)\n\nThis plugin allows you to integrate [Sentry](https://sentry.io/) seamlessly with your [Payload](https://github.com/payloadcms/payload) application.\n\n## What is Sentry?\n\nSentry is a powerful error tracking and performance monitoring tool that helps developers identify, diagnose, and resolve issues in their applications.\n\n\u003cBanner type=\"success\"\u003e\n Sentry does smart stuff with error data to make bugs easier to find and fix. - [sentry.io](https://sentry.io/)\n\u003c/Banner\u003e\n\nThis multi-faceted software offers a range of features that will help you manage errors with greater ease and ultimately ensure your application is running smoothly:\n\n## Core Features\n\n- **Error Tracking**: Instantly captures and logs errors as they occur in your application\n- **Performance Monitoring**: Tracks application performance to identify slowdowns and bottlenecks\n- **Detailed Reports**: Provides comprehensive insights into errors, including stack traces and context\n- **Alerts and Notifications**: Send and customize event-triggered notifications\n- **Issue Grouping, Filtering and Search**: Automatically groups similar errors, and allows filtering and searching issues by custom criteria\n- **Breadcrumbs**: Records user actions and events leading up to an error\n- **Integrations**: Connects with various tools and services for enhanced workflow and issue management\n\n\u003cBanner type=\"info\"\u003e\n This plugin is completely open-source and the [source code can be found here](https://github.com/payloadcms/payload/tree/main/packages/plugin-sentry). If you need help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a bug, please [open a new issue](https://github.com/payloadcms/payload/issues/new?assignees=\u0026labels=plugin%3A%20seo\u0026template=bug_report.md\u0026title=plugin-sentry%3A) with as much detail as possible.\n\u003c/Banner\u003e\n\n## Installation\n\nInstall the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):\n\n```bash\n pnpm add @payloadcms/plugin-sentry\n```\n\n## Sentry for Next.js setup\nThis plugin requires to complete the [Sentry + Next.js setup](https://docs.sentry.io/platforms/javascript/guides/nextjs/) before.\n\nYou can use either the [automatic setup](https://docs.sentry.io/platforms/javascript/guides/nextjs/#install) with the installation wizard:\n```sh\nnpx @sentry/wizard@latest -i nextjs\n```\nOr the [Manual Setup](https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/)\n\n## Basic Usage\n\nIn the `plugins` array of your [Payload Config](../configuration/overview), call the plugin and pass in your Sentry DSN as an option.\n\n```ts\nimport { buildConfig } from 'payload'\nimport { sentryPlugin } from '@payloadcms/plugin-sentry'\nimport { Pages, Media } from './collections'\n\nimport * as Sentry from '@sentry/nextjs'\n\nconst config = buildConfig({\n collections: [Pages, Media],\n plugins: [\n sentryPlugin({\n Sentry,\n }),\n ],\n})\n\nexport default config\n```\n\n## Options\n\n- `Sentry` : Sentry | **required**\n\n The `Sentry` instance\n\n\u003cBanner type=\"warning\"\u003e\n Make sure to complete the [Sentry for Next.js Setup](#sentry-for-nextjs-setup) before.\n\u003c/Banner\u003e\n\n- `enabled`: boolean | optional\n\n Set to false to disable the plugin. Defaults to `true`.\n\n- `context`: `(args: ContextArgs) =\u003e Partial\u003cScopeContext\u003e | Promise\u003cPartial\u003cScopeContext\u003e\u003e`\n\n Pass additional [contextual data](https://docs.sentry.io/platforms/javascript/enriching-events/context/#passing-context-directly) to Sentry\n\n- `captureErrors`: number[] | optional\n\n By default, `Sentry.errorHandler` will capture only errors with a status code of 500 or higher. To capture additional error codes, pass the values as numbers in an array.\n\n### Example\n\nConfigure any of these options by passing them to the plugin:\n\n```ts\nimport { buildConfig } from 'payload'\nimport { sentryPlugin } from '@payloadcms/plugin-sentry'\n\nimport * as Sentry from '@sentry/nextjs'\n\nimport { Pages, Media } from './collections'\n\nconst config = buildConfig({\n collections: [Pages, Media],\n plugins: [\n sentryPlugin({\n options: {\n captureErrors: [400, 403],\n context: ({ defaultContext, req }) =\u003e {\n return {\n ...defaultContext,\n tags: {\n locale: req.locale,\n },\n }\n },\n debug: true,\n },\n Sentry,\n }),\n ],\n})\n\nexport default config\n```\n\n## TypeScript\n\nAll types can be directly imported:\n\n```ts\nimport { PluginOptions } from '@payloadcms/plugin-sentry'\n```\n"])</script><script>self.__next_f.push([1,"9f:T35cb,"])</script><script>self.__next_f.push([1,"\n[![NPM](https://img.shields.io/npm/v/@payloadcms/plugin-stripe)](https://www.npmjs.com/package/@payloadcms/plugin-stripe)\n\nWith this plugin you can easily integrate [Stripe](https://stripe.com) into Payload. Simply provide your Stripe credentials and this plugin will open up a two-way communication channel between the two platforms. This enables you to easily sync data back and forth, as well as proxy the Stripe REST API through Payload's [Access Control](../access-control/overview). Use this plugin to completely offload billing to Stripe and retain full control over your application's data.\n\nFor example, you might be building an e-commerce or SaaS application, where you have a `products` or a `plans` collection that requires either a one-time payment or a subscription. You can to tie each of these products to Stripe, then easily subscribe to billing-related events to perform your application's business logic, such as active purchases or subscription cancellations.\n\nTo build a checkout flow on your front-end you can either use [Stripe Checkout](https://stripe.com/payments/checkout), or you can also build a completely custom checkout experience from scratch using [Stripe Web Elements](https://stripe.com/docs/payments/elements). Then to build fully custom, secure customer dashboards, you can leverage Payload's Access Control to restrict access to your Stripe resources so your users never have to leave your site to manage their accounts.\n\nThe beauty of this plugin is the entirety of your application's content and business logic can be handled in Payload while Stripe handles solely the billing and payment processing. You can build a completely proprietary application that is endlessly customizable and extendable, on APIs and databases that you own. Hosted services like Shopify or BigCommerce might fracture your application's content then charge you for access.\n\n\u003cBanner type=\"info\"\u003e\n This plugin is completely open-source and the [source code can be found\n here](https://github.com/payloadcms/payload/tree/main/packages/plugin-stripe). If you need help,\n check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a\n bug, please [open a new\n issue](https://github.com/payloadcms/payload/issues/new?assignees=\u0026labels=plugin%3A%20stripe\u0026template=bug_report.md\u0026title=plugin-stripe%3A)\n with as much detail as possible.\n\u003c/Banner\u003e\n\n## Core features\n\n- Hides your Stripe credentials when shipping SaaS applications\n- Allows restricted keys through [Payload access control](../access-control/overview)\n- Enables a two-way communication channel between Stripe and Payload\n- Proxies the [Stripe REST API](https://stripe.com/docs/api)\n- Proxies [Stripe webhooks](https://stripe.com/docs/webhooks)\n- Automatically syncs data between the two platforms\n\n## Installation\n\nInstall the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):\n\n```bash\n pnpm add @payloadcms/plugin-stripe\n```\n\n## Basic Usage\n\nIn the `plugins` array of your [Payload Config](../configuration/overview), call the plugin with [options](#options):\n\n```ts\nimport { buildConfig } from 'payload'\nimport { stripePlugin } from '@payloadcms/plugin-stripe'\n\nconst config = buildConfig({\n plugins: [\n stripePlugin({\n stripeSecretKey: process.env.STRIPE_SECRET_KEY,\n }),\n ],\n})\n\nexport default config\n```\n\n### Options\n\n| Option | Type | Default | Description |\n| ------------------------------ | ------------------ | ----------- | ------------------------------------------------------------------------------------------------------------------------ |\n| `stripeSecretKey` \\* | string | `undefined` | Your Stripe secret key |\n| `stripeWebhooksEndpointSecret` | string | `undefined` | Your Stripe webhook endpoint secret |\n| `rest` | boolean | `false` | When `true`, opens the `/api/stripe/rest` endpoint |\n| `webhooks` | object \\| function | `undefined` | Either a function to handle all webhooks events, or an object of Stripe webhook handlers, keyed to the name of the event |\n| `sync` | array | `undefined` | An array of sync configs |\n| `logs` | boolean | `false` | When `true`, logs sync events to the console as they happen |\n\n_\\* An asterisk denotes that a property is required._\n\n## Endpoints\n\nThe following custom endpoints are automatically opened for you:\n\n| Endpoint | Method | Description |\n| ---------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `/api/stripe/rest` | `POST` | Proxies the [Stripe REST API](https://stripe.com/docs/api) behind [Payload access control](../access-control/overview) and returns the result. See the [REST Proxy](#stripe-rest-proxy) section for more details. |\n| `/api/stripe/webhooks` | `POST` | Handles all Stripe webhook events |\n\n##### Stripe REST Proxy\n\nIf `rest` is true, proxies the [Stripe REST API](https://stripe.com/docs/api) behind [Payload access control](../access-control/overview) and returns the result. This flag should only be used for local development, see the security note below for more information.\n\n```ts\nconst res = await fetch(`/api/stripe/rest`, {\n method: 'POST',\n credentials: 'include',\n headers: {\n 'Content-Type': 'application/json',\n // Authorization: `JWT ${token}` // NOTE: do this if not in a browser (i.e. curl or Postman)\n },\n body: JSON.stringify({\n stripeMethod: 'stripe.subscriptions.list',\n stripeArgs: [\n {\n customer: 'abc',\n },\n ],\n }),\n})\n```\n\nIf you need to proxy the API server-side, use the [stripeProxy](#node) function.\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n The `/api` part of these routes may be different based on the settings defined in your Payload\n config.\n\u003c/Banner\u003e\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eWarning:\u003c/strong\u003e\n \u003cbr /\u003e\n Opening the REST proxy endpoint in production is a potential security risk. Authenticated users will have open access to the Stripe REST API. In production, open your own endpoint and use the [stripeProxy](#node) function to proxy the Stripe API server-side.\n\u003c/Banner\u003e\n\n## Webhooks\n\n[Stripe webhooks](https://stripe.com/docs/webhooks) are used to sync from Stripe to Payload. Webhooks listen for events on your Stripe account so you can trigger reactions to them. Follow the steps below to enable webhooks.\n\nDevelopment:\n\n1. Login using Stripe cli `stripe login`\n1. Forward events to localhost `stripe listen --forward-to localhost:3000/api/stripe/webhooks`\n1. Paste the given secret into your `.env` file as `STRIPE_WEBHOOKS_ENDPOINT_SECRET`\n\nProduction:\n\n1. Login and [create a new webhook](https://dashboard.stripe.com/test/webhooks/create) from the Stripe dashboard\n1. Paste `YOUR_DOMAIN_NAME/api/stripe/webhooks` as the \"Webhook Endpoint URL\"\n1. Select which events to broadcast\n1. Paste the given secret into your `.env` file as `STRIPE_WEBHOOKS_ENDPOINT_SECRET`\n1. Then, handle these events using the `webhooks` portion of this plugin's config:\n\n```ts\nimport { buildConfig } from 'payload'\nimport stripePlugin from '@payloadcms/plugin-stripe'\n\nconst config = buildConfig({\n plugins: [\n stripePlugin({\n stripeSecretKey: process.env.STRIPE_SECRET_KEY,\n stripeWebhooksEndpointSecret: process.env.STRIPE_WEBHOOKS_ENDPOINT_SECRET,\n webhooks: {\n 'customer.subscription.updated': ({ event, stripe, stripeConfig }) =\u003e {\n // do something...\n },\n },\n // NOTE: you can also catch all Stripe webhook events and handle the event types yourself\n // webhooks: (event, stripe, stripeConfig) =\u003e {\n // switch (event.type): {\n // case 'customer.subscription.updated': {\n // // do something...\n // break;\n // }\n // default: {\n // break;\n // }\n // }\n // }\n }),\n ],\n})\n\nexport default config\n```\n\nFor a full list of available webhooks, see [here](https://stripe.com/docs/cli/trigger#trigger-event).\n\n## Node\n\nOn the server you should interface with Stripe directly using the [stripe](https://www.npmjs.com/package/stripe) npm module. That might look something like this:\n\n```ts\nimport Stripe from 'stripe'\n\nconst stripeSecretKey = process.env.STRIPE_SECRET_KEY\nconst stripe = new Stripe(stripeSecretKey, { apiVersion: '2022-08-01' })\n\nexport const MyFunction = async () =\u003e {\n try {\n const customer = await stripe.customers.create({\n email: data.email,\n })\n\n // do something...\n } catch (error) {\n console.error(error.message)\n }\n}\n```\n\nAlternatively, you can interface with the Stripe using the `stripeProxy`, which is exactly what the `/api/stripe/rest` endpoint does behind-the-scenes. Here's the same example as above, but piped through the proxy:\n\n```ts\nimport { stripeProxy } from '@payloadcms/plugin-stripe'\n\nexport const MyFunction = async () =\u003e {\n try {\n const customer = await stripeProxy({\n stripeSecretKey: process.env.STRIPE_SECRET_KEY,\n stripeMethod: 'customers.create',\n stripeArgs: [\n {\n email: data.email,\n },\n ],\n })\n\n if (customer.status === 200) {\n // do something...\n }\n\n if (customer.status \u003e= 400) {\n throw new Error(customer.message)\n }\n } catch (error) {\n console.error(error.message)\n }\n}\n```\n\n## Sync\n\nThis option will setup a basic sync between Payload collections and Stripe resources for you automatically. It will create all the necessary hooks and webhooks handlers, so the only thing you have to do is map your Payload fields to their corresponding Stripe properties. As documents are created, updated, and deleted from either Stripe or Payload, the changes are reflected on either side.\n\n\u003cBanner type=\"info\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n If you wish to enable a _two-way_ sync, be sure to setup [`webhooks`](#webhooks) and pass the\n `stripeWebhooksEndpointSecret` through your config.\n\u003c/Banner\u003e\n\n```ts\nimport { buildConfig } from 'payload'\nimport stripePlugin from '@payloadcms/plugin-stripe'\n\nconst config = buildConfig({\n plugins: [\n stripePlugin({\n stripeSecretKey: process.env.STRIPE_SECRET_KEY,\n stripeWebhooksEndpointSecret: process.env.STRIPE_WEBHOOKS_ENDPOINT_SECRET,\n sync: [\n {\n collection: 'customers',\n stripeResourceType: 'customers',\n stripeResourceTypeSingular: 'customer',\n fields: [\n {\n fieldPath: 'name', // this is a field on your own Payload Config\n stripeProperty: 'name', // use dot notation, if applicable\n },\n ],\n },\n ],\n }),\n ],\n})\n\nexport default config\n```\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e\n \u003cbr /\u003e\n Due to limitations in the Stripe API, this currently only works with top-level fields. This is\n because every Stripe object is a separate entity, making it difficult to abstract into a simple\n reusable library. In the future, we may find a pattern around this. But for now, cases like that\n will need to be hard-coded.\n\u003c/Banner\u003e\n\nUsing `sync` will do the following:\n\n- Adds and maintains a `stripeID` read-only field on each collection, this is a field generated _by Stripe_ and used as a cross-reference\n- Adds a direct link to the resource on Stripe.com\n- Adds and maintains an `skipSync` read-only flag on each collection to prevent infinite syncs when hooks trigger webhooks\n- Adds the following hooks to each collection:\n - `beforeValidate`: `createNewInStripe`\n - `beforeChange`: `syncExistingWithStripe`\n - `afterDelete`: `deleteFromStripe`\n- Handles the following Stripe webhooks\n - `STRIPE_TYPE.created`: `handleCreatedOrUpdated`\n - `STRIPE_TYPE.updated`: `handleCreatedOrUpdated`\n - `STRIPE_TYPE.deleted`: `handleDeleted`\n\n## TypeScript\n\nAll types can be directly imported:\n\n```ts\nimport {\n StripeConfig,\n StripeWebhookHandler,\n StripeProxy,\n ...\n} from '@payloadcms/plugin-stripe/types';\n```\n\n## Examples\n\nThe [Templates Directory](https://github.com/payloadcms/payload/tree/main/templates) contains an official [E-commerce Template](https://github.com/payloadcms/payload/tree/main/templates/ecommerce) which demonstrates exactly how to configure this plugin in Payload and implement it on your front-end. You can also check out [How to Build An E-Commerce Site With Next.js](https://payloadcms.com/blog/how-to-build-an-e-commerce-site-with-nextjs) post for a bit more context around this template.\n"])</script><script>self.__next_f.push([1,"a0:T5e2,\nPayload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated so you can easily decipher precisely what is going on.\n\n- [Auth](https://github.com/payloadcms/payload/tree/main/examples/auth)\n- [Custom Components](https://github.com/payloadcms/payload/tree/main/examples/custom-components)\n- [Draft Preview](https://github.com/payloadcms/payload/tree/main/examples/draft-preview)\n- [Email](https://github.com/payloadcms/payload/tree/main/examples/email)\n- [Form Builder](https://github.com/payloadcms/payload/tree/main/examples/form-builder)\n- [Live Preview](https://github.com/payloadcms/payload/tree/main/examples/live-preview)\n- [Multi-tenant](https://github.com/payloadcms/payload/tree/main/examples/multi-tenant)\n- [Tailwind / Shadcn-ui](https://github.com/payloadcms/payload/tree/main/examples/tailwind-shadcn-ui)\n- [Tests](https://github.com/payloadcms/payload/tree/main/examples/testing)\n- [White-label Admin UI](https://github.com/payloadcms/payload/tree/main/examples/whitelabel)\n\nWe are adding new examples every day, so if your particular use case is not demonstrated in any existing example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.\na1:Tf2b,"])</script><script>self.__next_f.push([1,"\n[Vercel Content Link](https://vercel.com/docs/workflow-collaboration/edit-mode#content-link) will allow your editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it. This requires no changes to your front-end code and very few changes to your Payload Config.\n\n![Versions](/images/docs/vercel-visual-editing.jpg)\n\n\u003cBanner type=\"warning\"\u003e\n Vercel Content Link is an enterprise-only feature and only available for deployments hosted on\n Vercel. If you are an existing enterprise customer, [contact our sales\n team](https://payloadcms.com/for-enterprise) for help with your integration.\n\u003c/Banner\u003e\n\n## How it works\n\nTo power Vercel Content Link, Payload embeds Content Source Maps into its API responses. Content Source Maps are invisible, encoded JSON values that include a link back to the field in the CMS that generated the content. When rendered on the page, Vercel detects and decodes these values to display the Content Link interface.\n\nFor full details on how the encoding and decoding algorithm works, check out [`@vercel/stega`](https://www.npmjs.com/package/@vercel/stega).\n\n## Getting Started\n\nSetting up Payload with Vercel Content Link is easy. First, install the `@payloadcms/plugin-csm` plugin into your project. This plugin requires an API key to install, [contact our sales team](https://payloadcms.com/for-enterprise) if you don't already have one.\n\n```bash\nnpm i @payloadcms/plugin-csm\n```\n\nThen in the `plugins` array of your Payload Config, call the plugin and enable any collections that require Content Source Maps.\n\n```ts\nimport { buildConfig } from \"payload/config\"\nimport contentSourceMaps from \"@payloadcms/plugin-csm\"\n\nconst config = buildConfig({\n collections: [\n {\n slug: \"pages\",\n fields: [\n {\n name: 'slug',\n type: 'text',\n },\n {\n name: 'title,'\n type: 'text',\n },\n ],\n },\n ],\n plugins: [\n contentSourceMaps({\n collections: [\"pages\"],\n }),\n ],\n})\n\nexport default config\n```\n\nNow in your Next.js app, include the `?encodeSourceMaps=true` parameter in any of your API requests. For performance reasons, this should only be done when in draft mode or on preview deployments.\n\n```ts\nif (isDraftMode || process.env.VERCEL_ENV === 'preview') {\n const res = await fetch(\n `${process.env.NEXT_PUBLIC_PAYLOAD_CMS_URL}/api/pages?where[slug][equals]=${slug}\u0026encodeSourceMaps=true`,\n )\n}\n```\n\nAnd that's it! You are now ready to enter Edit Mode and begin visually editing your content.\n\n#### Edit Mode\n\nTo see Content Link on your site, you first need to visit any preview deployment on Vercel and login using the Vercel Toolbar. When Content Source Maps are detected on the page, a pencil icon will appear in the toolbar. Clicking this icon will enable Edit Mode, highlighting all editable fields on the page in blue.\n\n![Versions](/images/docs/vercel-toolbar.jpg)\n\n## Troubleshooting\n\n### Date Fields\n\nThe plugin does not encode `date` fields by default, but for some cases like text that uses negative CSS letter-spacing, it may be necessary to split the encoded data out from the rendered text. This way you can safely use the cleaned data as expected.\n\n```ts\nimport { vercelStegaSplit } from '@vercel/stega'\nconst { cleaned, encoded } = vercelStegaSplit(text)\n```\n\n### Blocks and array fields\n\nAll `blocks` and `array` fields by definition do not have plain text strings to encode. For this reason, they are given an additional `_encodedSourceMap` property, which you can use to enable Content Link on entire _sections_ of your site. You can then specify the editing container by adding the `data-vercel-edit-target` HTML attribute to any top-level element of your block.\n\n```ts\n\u003cdiv data-vercel-edit-target\u003e\n \u003cspan style={{ display: \"none\" }}\u003e{_encodedSourceMap}\u003c/span\u003e\n {children}\n\u003c/div\u003e\n```\n"])</script><script>self.__next_f.push([1,"a2:Tbb4,"])</script><script>self.__next_f.push([1,"\nA deployment solution specifically designed for Node.js + MongoDB applications, offering seamless deployment of your entire stack in one place. You can get started in minutes with a one-click template or bring your own codebase with you.\n\nPayload Cloud offers various plans tailored to meet your specific needs, including a MongoDB Atlas database, S3 file storage, and email delivery powered by [Resend](https://resend.com). To see a full breakdown of features and plans, see our [Cloud Pricing page](https://payloadcms.com/cloud-pricing).\n\nTo get started, you first need to create an account. Head over to [the login screen](https://payloadcms.com/login) and **Register for Free**.\n\n\u003cBanner type=\"success\"\u003e\n To create your first project, you can either select [a template](#starting-from-a-template) or\n [import an existing project](#importing-from-an-existing-codebase) from GitHub.\n\u003c/Banner\u003e\n\n## Starting from a Template\n\nTemplates come preconfigured and provide a one-click solution to quickly deploy a new application.\n\n![Screen for creating a new project from a template](https://payloadcms.com/images/docs/cloud/create-from-template.jpg)\n_Creating a new project from a template._\n\nAfter creating an account, select your desired template from the Projects page. At this point, you need to connect to authorize the Payload Cloud application with your GitHub account. Click Continue with GitHub and follow the prompts to authorize the app.\n\nNext, select your `GitHub Scope`. If you belong to multiple organizations, they will show up here. If you do not see the organization you are looking for, you may need to adjust your GitHub app permissions.\n\nAfter selecting your scope, create a unique `repository name` and select whether you want your repository to be public or private on GitHub.\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e Public repositories can be accessed by anyone online, while private\n repositories grant access only to you and anyone you explicitly authorize.\n\u003c/Banner\u003e\n\nOnce you are ready, click **Create Project**. This will clone the selected template to a new repository in your GitHub account, and take you to the configuration page to set up your project for deployment.\n\n## Importing from an Existing Codebase\n\nPayload Cloud works for any Node.js + MongoDB app. From the New Project page, select **import an existing Git codebase**. Choose the organization and select the repository you want to import. From here, you will be taken to the configuration page to set up your project for deployment.\n\n![Screen for creating a new project from an existing repository](https://payloadcms.com/images/docs/cloud/create-from-existing.jpg)\n_Creating a new project from an existing repository._\n\n\u003cBanner type=\"warning\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e In order to make use of the features of Payload Cloud in your own codebase,\n you will need to add the [Cloud Plugin](https://github.com/payloadcms/payload/tree/main/packages/payload-cloud) to your\n Payload app.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"a3:T1365,"])</script><script>self.__next_f.push([1,"\n## Select your plan\n\nOnce you have created a project, you will need to select your plan. This will determine the resources that are allocated to your project and the features that are available to you.\n\n\u003cBanner type=\"success\"\u003e\n Note: All Payload Cloud teams that deploy a project require a card on file. This helps us prevent\n fraud and abuse on our platform. If you select a plan with a free trial, you will not be charged\n until your trial period is over. We’ll remind you 7 days before your trial ends and you can cancel\n anytime.\n\u003c/Banner\u003e\n\n## Project Details\n\n| Option | Description |\n| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Region** | Select the region closest to your audience. This will ensure the fastest communication between your data and your client. |\n| **Project Name** | A name for your project. You can change this at any time. |\n| **Project Slug** | Choose a unique slug to identify your project. This needs to be unique for your team and you can change it any time. |\n| **Team** | Select the team you want to create the project under. If this is your first project, a personal team will be created for you automatically. You can modify your team settings and invite new members at any time from the Team Settings page. |\n\n## Build Settings\n\nIf you are deploying a new project from a template, the following settings will be automatically configured for you. If you are using your own repository, you need to make sure your build settings are accurate for your project to deploy correctly.\n\n| Option | Description |\n| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Root Directory** | The folder where your `package.json` file lives. |\n| **Install Command** | The command used to install your modules, for example: `yarn install` or `npm install` |\n| **Build Command** | The command used to build your application, for example: `yarn build` or `npm run build` |\n| **Serve Command** | The command used to serve your application, for example: `yarn serve` or `npm run serve` |\n| **Branch to Deploy** | Select the branch of your repository that you want to deploy from. This is the branch that will be used to build your project when you commit new changes. |\n| **Default Domain** | Set a default domain for your project. This must be unique and you will not able to change it. You can always add a custom domain later in your project settings. |\n\n## Environment Variables\n\nAny of the features in Payload Cloud that require environment variables will automatically be provided to your application. If your app requires any custom environment variables, you can set them here.\n\n\u003cBanner type=\"warning\"\u003e\n Note: For security reasons, any variables you wish to provide to the [Admin Panel](../admin/overview) must be prefixed\n with `NEXT_PUBLIC_`. Learn more\n [here](../configuration/environment-vars).\n\u003c/Banner\u003e\n\n## Payment\n\nPayment methods can be set per project and can be updated any time. You can use team’s default payment method, or add a new one. Modify your payment methods in your Project settings / Team settings.\n\n\u003cBanner type=\"success\"\u003e\n \u003cstrong\u003eNote:\u003c/strong\u003e All Payload Cloud teams that deploy a project require a card on file. This\n helps us prevent fraud and abuse on our platform. If you select a plan with a free trial, you will\n not be charged until your trial period is over. We’ll remind you 7 days before your trial ends and\n you can cancel anytime.\n\u003c/Banner\u003e\n"])</script><script>self.__next_f.push([1,"a4:T68c,\n\u003cBanner\u003e\n Within Payload Cloud, the team management feature offers you the ability to manage your\n organization, team members, billing, and subscription settings.\n\u003c/Banner\u003e\n\n![Payload Cloud Team Settings](https://payloadcms.com/images/docs/cloud/team-settings.jpg)\n_A screenshot of the Team Settings page._\n\n## Members\n\nEach team has members that can interact with your projects. You can invite multiple people to your team and each individual can belong to more than one team. You can assign them either `owner` or `user` permissions. Owners are able to make admin-only changes, such as deleting projects, and editing billing information.\n\n## Adding Members\n\nTo add a new member to your team, visit your Team’s Settings page, and click “Invite Teammate”. You can then add their email address, and assign their role. Press “Save” to send the invitations, which will send an email to the invited team member where they can create a new account.\n\n## Billing\n\nUsers can update billing settings and subscriptions for any teams where they are designated as an `owner`. To make updates to the team’s payment methods, visit the Billing page under the Team Settings tab. You can add new cards, delete cards, and set a payment method as a default. The default payment method will be used in the event that another payment method fails.\n\n## Subscriptions\n\nFrom the Subscriptions page, a team owner can see all current plans for their team. From here, you can see the price of each plan, if there is an active trial, and when you will be billed next.\n\n## Invoices\n\nThe Invoices page will you show you the invoices for your account, as well as the status on their payment.\na5:T18ba,"])</script><script>self.__next_f.push([1,"\n## Overview\n\n\u003cBanner\u003e\n The overview tab shows your most recent deployment, along with build and deployment logs. From\n here, you can see your live URL, deployment details like timestamps and commit hash, as well as\n the status of your deployment. You can also trigger a redeployment manually, which will rebuild\n your project using the current configuration.\n\u003c/Banner\u003e\n\n![Payload Cloud Overview Page](https://payloadcms.com/images/docs/cloud/overview-page.jpg)\n_A screenshot of the Overview page for a Cloud project._\n\n## Database\n\nYour Payload Cloud project comes with a MongoDB serverless Atlas DB instance or a Dedicated Atlas cluster, depending on your plan. To interact with your cloud database, you will be provided with a MongoDB connection string. This can be found under the **Database** tab of your project.\n\n`mongodb+srv://your_connection_string`\n\n## File Storage\n\nPayload Cloud gives you S3 file storage backed by Cloudflare as a CDN, and this plugin extends Payload so that all of your media will be stored in S3 rather than locally.\n\nAWS Cognito is used for authentication to your S3 bucket. The [Payload Cloud Plugin](https://github.com/payloadcms/payload/tree/main/packages/payload-cloud) will automatically pick up these values. These values are only if you'd like to access your files directly, outside of Payload Cloud.\n\n### Accessing Files Outside of Payload Cloud\n\nIf you'd like to access your files outside of Payload Cloud, you'll need to retrieve some values from your project's settings and put them into your environment variables. In Payload Cloud, navigate to the File Storage tab and copy the values using the copy button. Put these values in your .env file. Also copy the Cognito Password value separately and put into your .env file as well.\n\nWhen you are done, you should have the following values in your .env file:\n\n```env\nPAYLOAD_CLOUD=true\nPAYLOAD_CLOUD_ENVIRONMENT=prod\nPAYLOAD_CLOUD_COGNITO_USER_POOL_CLIENT_ID=\nPAYLOAD_CLOUD_COGNITO_USER_POOL_ID=\nPAYLOAD_CLOUD_COGNITO_IDENTITY_POOL_ID=\nPAYLOAD_CLOUD_PROJECT_ID=\nPAYLOAD_CLOUD_BUCKET=\nPAYLOAD_CLOUD_BUCKET_REGION=\nPAYLOAD_CLOUD_COGNITO_PASSWORD=\n```\n\nThe plugin will pick up these values and use them to access your files.\n\n## Build Settings\n\nYou can update settings from your Project’s Settings tab. Changes to your build settings will trigger a redeployment of your project.\n\n## Environment Variables\n\nFrom the Environment Variables page of the Settings tab, you can add, update and delete variables for use in your project. Like build settings, these changes will trigger a redeployment of your project.\n\n\u003cBanner\u003e\n Note: For security reasons, any variables you wish to provide to the [Admin Panel](../admin/overview) must be prefixed with `NEXT_PUBLIC_`. [More details](../configuration/environment-vars).\n\u003c/Banner\u003e\n\n## Custom Domains\n\nWith Payload Cloud, you can add custom domain names to your project. To do so, first go to the Domains page of the Settings tab of your project. Here you can see your default domain. To add a new domain, type in the domain name you wish to use.\n\n\u003cBanner\u003e\n Note: do not include the protocol (http:// or https://) or any paths (/page). Only include the\n domain name and extension, and optionally a subdomain. - your-domain.com - backend.your-domain.com\n\u003c/Banner\u003e\n\nOnce you click save, a DNS record will be generated for your domain name to point to your live project. Add this record into your DNS provider’s records, and once the records are resolving properly (this can take 1hr to 48hrs in some cases), your domain will now to point to your live project.\n\nYou will also need to configure your Payload project to use your specified domain. In your `payload.config.ts` file, specify your `serverURL` with your domain:\n\n```ts\nexport default buildConfig({\n serverURL: 'https://example.com',\n // the rest of your config,\n})\n```\n\n## Email\n\nPowered by [Resend](https://resend.com), Payload Cloud comes with integrated email support out of the box. No configuration is needed, and you can use `payload.sendEmail()` to send email right from your Payload app. To learn more about sending email with Payload, checkout the [Email Configuration](../email/overview) overview.\n\nIf you are on the Pro or Enterprise plan, you can add your own custom Email domain name. From the Email page of your project’s Settings, add the domain you wish to use for email delivery. This will generate a set of DNS records. Add these records to your DNS provider and click verify to check that your records are resolving properly. Once verified, your emails will now be sent from your custom domain name.\n\n## Developing Locally\n\nTo make changes to your project, you will need to clone the repository defined in your project settings to your local machine. In order to run your project locally, you will need configure your local environment first. Refer to your repository’s `README.md` file to see the steps needed for your specific template.\n\nFrom there, you are ready to make updates to your project. When you are ready to make your changes live, commit your changes to the branch you specified in your Project settings, and your application will automatically trigger a redeploy and build from your latest commit.\n\n## Cloud Plugin\n\nProjects generated from a template will come pre-configured with the official Cloud Plugin, but if you are using your own repository you will need to add this into your project. To do so, add the plugin to your Payload Config:\n\n`yarn add @payloadcms/payload-cloud`\n\n```js\nimport { payloadCloudPlugin } from '@payloadcms/payload-cloud'\nimport { buildConfig } from 'payload'\n\nexport default buildConfig({\n plugins: [payloadCloudPlugin()],\n // rest of config\n})\n```\n\n\u003cBanner type=\"warning\"\u003e\n **Note:** If your Payload Config already has an email with transport, this will take precedence\n over Payload Cloud's email service.\n\u003c/Banner\u003e\n\n\u003cBanner type=\"info\"\u003e\n Good to know: the Payload Cloud Plugin was previously named `@payloadcms/plugin-cloud`. If you are\n using this plugin, you should update to the new package name.\n\u003c/Banner\u003e\n\n#### **Optional configuration**\n\nIf you wish to opt-out of any Payload cloud features, the plugin also accepts options to do so.\n\n```js\npayloadCloud({\n storage: false, // Disable file storage\n email: false, // Disable email delivery\n})\n```\n"])</script><script>self.__next_f.push([1,"a6:T2961,"])</script><script>self.__next_f.push([1,"\n\u003cBanner type=\"success\"\u003e\n So you've developed a Payload app, it's fully tested, and running great locally. Now it's time to\n launch. \u003cstrong\u003eAwesome! Great work!\u003c/strong\u003e Now, what's next?\n\u003c/Banner\u003e\n\nThere are many ways to deploy Payload to a production environment. When evaluating how you will deploy Payload, you need\nto consider these main aspects:\n\n1. [Basics](#basics)\n1. [Security](#security)\n1. [Your database](#database)\n1. [Permanent File Storage](#file-storage)\n1. [Docker](#docker)\n\nPayload can be deployed _anywhere that Next.js can run_ - including Vercel, Netlify, SST, DigitalOcean, AWS, and more. Because it's open source, you can self-host it.\n\nBut it's important to remember that most Payload projects will also need a database, file storage, an email provider, and a CDN. Make sure you have all of the requirements that your project needs, no matter what deployment platform you choose.\n\nOften, the easiest and fastest way to deploy Payload is to use [Payload Cloud](https://payloadcms.com/new) — where you get everything you need out of the box, including:\n\n1. A MongoDB Atlas database\n1. S3 file storage\n1. Resend email service\n1. Cloudflare CDN\n1. Blue / green deployments\n1. Logs\n1. And more\n\n## Basics\n\nPayload runs fully in Next.js, so the [Next.js build process](https://nextjs.org/docs/app/building-your-application/deploying) is used for building Payload. If you've used `create-payload-app` to create your project, executing the `build`\nnpm script will build Payload for production.\n\n## Security\n\nPayload features a suite of security features that you can rely on to strengthen your application's security. When\ndeploying to Production, it's a good idea to double-check that you are making proper use of each of them.\n\n### The Secret Key\n\nWhen you initialize Payload, you provide it with a `secret` property. This property should be impossible to guess and\nextremely difficult for brute-force attacks to crack. Make sure your Production `secret` is a long, complex string.\n\n### Double-check and thoroughly test all Access Control\n\nBecause _**you**_ are in complete control of who can do what with your data, you should double and triple-check that you\nwield that power responsibly before deploying to Production.\n\n\u003cBanner type=\"error\"\u003e\n \u003cstrong\u003e\n By default, all Access Control functions require that a user is successfully logged in to\n Payload to create, read, update, or delete data.\n \u003c/strong\u003e\n But, if you allow public user registration, for example, you will want to make sure that your\n access control functions are more strict - permitting\n {' '}\n \u003cstrong\u003e\n only appropriate users\n \u003c/strong\u003e\n {' '}\n to perform appropriate actions.\n\u003c/Banner\u003e\n\n### Running in Production\n\nDepending on where you deploy Payload, you may need to provide a start script to your deployment platform in order to start up Payload in production mode.\n\nNote that this is different than running `next dev`. Generally, Next.js apps come configured with a `start` script which runs `next start`.\n\n### Secure Cookie Settings\n\nYou should be using an SSL certificate for production Payload instances, which means you\ncan [enable secure cookies](../authentication/overview) in your Authentication-enabled Collection configs.\n\n### Preventing API Abuse\n\nPayload comes with a robust set of built-in anti-abuse measures, such as locking out users after X amount of failed\nlogin attempts, GraphQL query complexity limits, max `depth` settings, and\nmore. [Click here to learn more](../production/preventing-abuse).\n\n## Database\n\nPayload can be used with any Postgres database or MongoDB-compatible database including AWS DocumentDB or Azure Cosmos DB. Make sure your production environment has access to the database that Payload uses.\n\nOut of the box, Payload templates pass the `process.env.DATABASE_URI` environment variable to its database adapters, so make sure you've got that environment variable (and all others that you use) assigned in your deployment platform.\n\n### DocumentDB\n\nWhen using AWS DocumentDB, you will need to configure connection options for authentication in the `connectOptions`\npassed to the `mongooseAdapter` . You also need to set `connectOptions.useFacet` to `false` to disable use of the\nunsupported `$facet` aggregation.\n\n### CosmosDB\n\nWhen using Azure Cosmos DB, an index is needed for any field you may want to sort on. To add the sort index for all\nfields that may be sorted in the admin UI use the \u003ca href=\"../configuration/overview\"\u003eindexSortableFields\u003c/a\u003e\nconfiguration option.\n\n## File storage\n\nIf you are using Payload to [manage file uploads](../upload/overview), you need to consider where your uploaded files\nwill be permanently stored. If you do not use Payload for file uploads, then this section does not impact your app\nwhatsoever.\n\n### Persistent vs Ephemeral Filesystems\n\nSome cloud app hosts such as [Heroku](https://heroku.com) use `ephemeral` file systems, which means that any files\nuploaded to your server only last until the server restarts or shuts down. Heroku and similar providers schedule\nrestarts and shutdowns without your control, meaning your uploads will accidentally disappear without any way to get\nthem back.\n\nAlternatively, persistent filesystems will never delete your files and can be trusted to reliably host uploads\nperpetually.\n\n**Popular cloud providers with ephemeral filesystems:**\n\n- Heroku\n- DigitalOcean Apps\n\n**Popular cloud providers with persistent filesystems:**\n\n- DigitalOcean Droplets\n- Amazon EC2\n- GoDaddy\n- Many other more traditional web hosts\n\n\u003cBanner type=\"error\"\u003e\n \u003cstrong\u003eWarning:\u003c/strong\u003e\n \u003cbr /\u003e\n If you rely on Payload's \u003cstrong\u003eUpload\u003c/strong\u003e functionality, make sure you either use a host\n with a persistent filesystem or have an integration with a third-party file host like Amazon S3.\n\u003c/Banner\u003e\n\n### Using cloud storage providers\n\nIf you don't use Payload's `upload` functionality, you can completely disregard this section.\n\nBut, if you do, and you still want to use an ephemeral filesystem provider, you can use one of Payload's official cloud storage plugins or write your own to save the files your users upload to a more permanent storage solution like Amazon S3 or DigitalOcean Spaces.\n\nPayload provides a list of official cloud storage adapters for you to use:\n\n- [Azure Blob Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-azure)\n- [Google Cloud Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-gcs)\n- [AWS S3](https://github.com/payloadcms/payload/tree/main/packages/storage-s3)\n- [Uploadthing](https://github.com/payloadcms/payload/tree/main/packages/storage-uploadthing)\n- [Vercel Blob Storage](https://github.com/payloadcms/payload/tree/main/packages/storage-vercel-blob)\n\nFollow the docs to configure any one of these storage providers. For local development, it might be handy to simply store uploads on your own computer, and then when it comes to production, simply enable the plugin for the cloud storage vendor of your choice.\n\n## Docker\n\nThis is an example of a multi-stage docker build of Payload for production. Ensure you are setting your environment\nvariables on deployment, like `PAYLOAD_SECRET`, `PAYLOAD_CONFIG_PATH`, and `DATABASE_URI` if needed.\n\n```dockerfile\n# From https://github.com/vercel/next.js/blob/canary/examples/with-docker/Dockerfile\n\nFROM node:18-alpine AS base\n\n# Install dependencies only when needed\nFROM base AS deps\n# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.\nRUN apk add --no-cache libc6-compat\nWORKDIR /app\n\n# Install dependencies based on the preferred package manager\nCOPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./\nRUN \\\n if [ -f yarn.lock ]; then yarn --frozen-lockfile; \\\n elif [ -f package-lock.json ]; then npm ci; \\\n elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm \u0026\u0026 pnpm i --frozen-lockfile; \\\n else echo \"Lockfile not found.\" \u0026\u0026 exit 1; \\\n fi\n\n\n# Rebuild the source code only when needed\nFROM base AS builder\nWORKDIR /app\nCOPY --from=deps /app/node_modules ./node_modules\nCOPY . .\n\n# Next.js collects completely anonymous telemetry data about general usage.\n# Learn more here: https://nextjs.org/telemetry\n# Uncomment the following line in case you want to disable telemetry during the build.\n# ENV NEXT_TELEMETRY_DISABLED 1\n\nRUN \\\n if [ -f yarn.lock ]; then yarn run build; \\\n elif [ -f package-lock.json ]; then npm run build; \\\n elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm \u0026\u0026 pnpm run build; \\\n else echo \"Lockfile not found.\" \u0026\u0026 exit 1; \\\n fi\n\n# Production image, copy all the files and run next\nFROM base AS runner\nWORKDIR /app\n\nENV NODE_ENV production\n# Uncomment the following line in case you want to disable telemetry during runtime.\n# ENV NEXT_TELEMETRY_DISABLED 1\n\nRUN addgroup --system --gid 1001 nodejs\nRUN adduser --system --uid 1001 nextjs\n\nCOPY --from=builder /app/public ./public\n\n# Set the correct permission for prerender cache\nRUN mkdir .next\nRUN chown nextjs:nodejs .next\n\n# Automatically leverage output traces to reduce image size\n# https://nextjs.org/docs/advanced-features/output-file-tracing\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./\nCOPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static\n\nUSER nextjs\n\nEXPOSE 3000\n\nENV PORT 3000\n\n# server.js is created by next build from the standalone output\n# https://nextjs.org/docs/pages/api-reference/next-config-js/output\nCMD HOSTNAME=\"0.0.0.0\" node server.js\n```\n\n## Docker Compose\n\nHere is an example of a docker-compose.yml file that can be used for development\n\n```yml\nversion: '3'\n\nservices:\n payload:\n image: node:18-alpine\n ports:\n - '3000:3000'\n volumes:\n - .:/home/node/app\n - node_modules:/home/node/app/node_modules\n working_dir: /home/node/app/\n command: sh -c \"corepack enable \u0026\u0026 corepack prepare pnpm@latest --activate \u0026\u0026 pnpm install \u0026\u0026 pnpm dev\"\n depends_on:\n - mongo\n # - postgres\n env_file:\n - .env\n\n # Ensure your DATABASE_URI uses 'mongo' as the hostname ie. mongodb://mongo/my-db-name\n mongo:\n image: mongo:latest\n ports:\n - '27017:27017'\n command:\n - --storageEngine=wiredTiger\n volumes:\n - data:/data/db\n logging:\n driver: none\n\n # Uncomment the following to use postgres\n # postgres:\n # restart: always\n # image: postgres:latest\n # volumes:\n # - pgdata:/var/lib/postgresql/data\n # ports:\n # - \"5432:5432\"\n\nvolumes:\n data:\n # pgdata:\n node_modules:\n```\n"])</script><script>self.__next_f.push([1,"a7:Te21,"])</script><script>self.__next_f.push([1,"\n## Introduction\n\nPayload has built-in security best practices that can be configured to your application-specific needs.\n\n## Limit Failed Login Attempts\n\nSet the max number of failed login attempts before a user account is locked out for a period of time. Set the `maxLoginAttempts` on the collections that feature Authentication to a reasonable but low number for your users to get in. Use the `lockTime` to set a number in milliseconds from the time a user fails their last allowed attempt that a user must wait to try again.\n\n## Max Depth\n\nQuerying a collection and automatically including related documents via `depth` incurs a performance cost. Also, it's possible that your configs may have circular relationships, meaning scenarios where an infinite amount of relationships might populate back and forth until your server times out and crashes. You can prevent any potential of depth-related issues by setting a `maxDepth` property on your Payload Config.. The maximum allowed depth should be as small as possible without interrupting dev experience, and it defaults to `10`.\n\n## Cross-Site Request Forgery (CSRF)\n\nCSRF prevention will verify the authenticity of each request to your API to prevent a malicious action from another site from authorized users. See how to configure CSRF [here](../authentication/overview#csrf-protection).\n\n## Cross Origin Resource Sharing (CORS)\n\nTo securely allow headless operation you will need to configure the allowed origins for requests to be able to use the Payload API. You can see how to set CORS as well as other Payload configuration settings [here](../configuration/overview)\n\n## Limiting GraphQL Complexity\n\nBecause GraphQL gives the power of query writing outside a server's control, someone with bad intentions might write a maliciously complex query and bog down your server. To prevent resource-intensive GraphQL requests, Payload provides a way specify complexity limits which are based on a complexity score that is calculated for each request.\n\nAny GraphQL request that is calculated to be too expensive is rejected. On the Payload Config, in `graphQL` you can set the `maxComplexity` value as an integer. For reference, the default complexity value for each added field is 1, and all `relationship` and `upload` fields are assigned a value of 10.\n\nIf you do not need GraphQL it is advised that you disable it altogether with the Payload Config by setting `graphQL.disable: true`. Should you wish to enable GraphQL again, you can remove this property or set it `false`, any time. By turning it off, Payload will bypass creating schemas from your collections and will not register the route.\n\n## Malicious File Uploads\n\nPayload does not execute uploaded files on the server, but depending on your setup it may be used to transmit and store potentially dangerous files. If your configuration allows file uploads there is the potential that a bad actor uploads a malicious file that is then served to other users. Consider the following ways to mitigate the risks.\n\nFirst, enable email [verification](../authentication/overview#email-verification) when users are allowed to register new accounts and add other bot prevention services.\n\nReview that `create` and `update` access on file upload collections are as restrictive as your application needs allow. Consider limiting `read` access of uploaded user's files and how you might limit user uploaded files from being served outside of Payload.\n\nYou can also add a [3rd party library](https://github.com/Cisco-Talos/clamav) to scan files in a [hook](../hooks/collections) or have antivirus software in place.\n"])</script><script>self.__next_f.push([1,"ab:T451,M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM19.0977 8C20.6641 8.2627 22.1914 8.72656 23.6367 9.38184C26.127 12.9844 27.3633 17.0479 26.9062 21.7363C25.2246 22.9678 23.3398 23.9023 21.3359 24.5C20.8848 23.9014 20.4863 23.2656 20.1445 22.5996C20.7969 22.3584 21.4258 22.0605 22.0254 21.7109C21.8691 21.6064 21.7148 21.4893 21.5645 21.3682C19.8242 22.1787 17.9238 22.5986 16 22.5986C14.0762 22.5986 12.1758 22.1787 10.4355 21.3682C10.2871 21.4814 10.1328 21.5977 9.97461 21.7109C10.2852 21.8926 10.6035 22.0596 10.9297 22.2119C11.2305 22.3535 11.5391 22.4824 11.8516 22.5977C11.5098 23.2637 11.1113 23.9004 10.6602 24.5C9.34961 24.1064 8.08984 23.5703 6.9043 22.9014C6.2793 22.5488 5.67383 22.1592 5.09375 21.7344C4.70312 17.6914 5.48242 13.5908 8.35547 9.38574C9.80273 8.73047 11.3281 8.26465 12.8965 8C13.1113 8.37988 13.3066 8.76953 13.4785 9.16992C14.3887 9.03418 15.3086 8.97266 16.2246 8.98535C16.9902 8.99512 17.7559 9.05664 18.5156 9.16992C18.6133 8.94434 18.7168 8.72266 18.8281 8.50391C18.9141 8.33398 19.0039 8.16602 19.0977 8Zac:T451,M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM19.0977 8C20.6641 8.2627 22.1914 8.72656 23.6367 9.38184C26.127 12.9844 27.3633 17.0479 26.9062 21.7363C25.2246 22.9678 23.3398 23.9023 21.3359 24.5C20.8848 23.9014 20.4863 23.2656 20.1445 22.5996C20.7969 22.3584 21.4258 22.0605 22.0254 21.7109C21.8691 21.6064 21.7148 21.4893 21.5645 21.3682C19.8242 22.1787 17.9238 22.5986 16 22.5986C14.0762 22.5986 12.1758 22.1787 10.4355 21.3682C10.2871 21.4814 10.1328 21.5977 9.97461 21.7109C10.2852 21.8926 10.6035 22.0596 10.9297 22.2119C11.2305 22.3535 11.5391 22.4824 11.8516 22.5977C11.5098 23.2637 11.1113 23.9004 10.6602 24.5C9.34961 24.1064 8.08984 23.5703 6.9043 22.9014C6.2793 22.5488 5.67383 22.1592 5.09375 21.7344C4.70312 17.6914 5.48242 13.5908 8.35547 9.38574C9.80273 8.73047 11.3281 8.26465 12.8965 8C13.1113 8.37988 13.3066 8.76953 13.4785 9.16992C14.3887 "])</script><script>self.__next_f.push([1,"9.03418 15.3086 8.97266 16.2246 8.98535C16.9902 8.99512 17.7559 9.05664 18.5156 9.16992C18.6133 8.94434 18.7168 8.72266 18.8281 8.50391C18.9141 8.33398 19.0039 8.16602 19.0977 8Zad:T451,M16 32C24.8359 32 32 24.8369 32 16C32 7.16309 24.8359 0 16 0C7.16406 0 0 7.16309 0 16C0 24.8369 7.16406 32 16 32ZM19.0977 8C20.6641 8.2627 22.1914 8.72656 23.6367 9.38184C26.127 12.9844 27.3633 17.0479 26.9062 21.7363C25.2246 22.9678 23.3398 23.9023 21.3359 24.5C20.8848 23.9014 20.4863 23.2656 20.1445 22.5996C20.7969 22.3584 21.4258 22.0605 22.0254 21.7109C21.8691 21.6064 21.7148 21.4893 21.5645 21.3682C19.8242 22.1787 17.9238 22.5986 16 22.5986C14.0762 22.5986 12.1758 22.1787 10.4355 21.3682C10.2871 21.4814 10.1328 21.5977 9.97461 21.7109C10.2852 21.8926 10.6035 22.0596 10.9297 22.2119C11.2305 22.3535 11.5391 22.4824 11.8516 22.5977C11.5098 23.2637 11.1113 23.9004 10.6602 24.5C9.34961 24.1064 8.08984 23.5703 6.9043 22.9014C6.2793 22.5488 5.67383 22.1592 5.09375 21.7344C4.70312 17.6914 5.48242 13.5908 8.35547 9.38574C9.80273 8.73047 11.3281 8.26465 12.8965 8C13.1113 8.37988 13.3066 8.76953 13.4785 9.16992C14.3887 9.03418 15.3086 8.97266 16.2246 8.98535C16.9902 8.99512 17.7559 9.05664 18.5156 9.16992C18.6133 8.94434 18.7168 8.72266 18.8281 8.50391C18.9141 8.33398 19.0039 8.16602 19.0977 8Z"])</script><script>self.__next_f.push([1,"20:[\"$\",\"div\",null,{\"className\":\"RenderDocs_wrap__HtZBM Gutter_leftGutter__UWO6t Gutter_rightGutter__qUzUU\",\"data-theme\":\"$undefined\",\"ref\":null,\"children\":[\"$\",\"$L32\",null,{\"children\":[[\"$\",\"div\",null,{\"className\":\"grid\",\"children\":[[\"$\",\"$L33\",null,{\"currentTopic\":\"configuration\",\"params\":{\"topic\":\"configuration\",\"doc\":\"localization\"},\"topics\":[{\"groupLabel\":\"Basics\",\"topics\":[{\"slug\":\"Getting-Started\",\"docs\":[{\"slug\":\"what-is-payload\",\"content\":\"$34\",\"desc\":\"Payload is a next-gen application framework that can be used as a Content Management System, enterprise tool framework, headless commerce platform, or digital asset management tool.\",\"headings\":[{\"id\":\"instant-backend-superpowers\",\"level\":3,\"text\":\"Instant backend superpowers\",\"anchor\":\"instant-backend-superpowers\"},{\"id\":\"open-source-deploy-anywhere-including-vercel\",\"level\":3,\"text\":\"Open source - deploy anywhere, including Vercel\",\"anchor\":\"open-source-deploy-anywhere-including-vercel\"},{\"id\":\"code-first-and-version-controlled\",\"level\":3,\"text\":\"Code-first and version controlled\",\"anchor\":\"code-first-and-version-controlled\"},{\"id\":\"fully-extensible\",\"level\":3,\"text\":\"Fully extensible\",\"anchor\":\"fully-extensible\"},{\"id\":\"use-cases\",\"level\":2,\"text\":\"Use Cases\",\"anchor\":\"use-cases\"},{\"id\":\"headless-cms\",\"level\":3,\"text\":\"Headless CMS\",\"anchor\":\"headless-cms\"},{\"id\":\"enterprise-tool\",\"level\":3,\"text\":\"Enterprise Tool\",\"anchor\":\"enterprise-tool\"},{\"id\":\"headless-commerce\",\"level\":3,\"text\":\"Headless Commerce\",\"anchor\":\"headless-commerce\"},{\"id\":\"digital-asset-management\",\"level\":3,\"text\":\"Digital Asset Management\",\"anchor\":\"digital-asset-management\"},{\"id\":\"choosing-a-framework\",\"level\":2,\"text\":\"Choosing a Framework\",\"anchor\":\"choosing-a-framework\"},{\"id\":\"when-payload-might-be-for-you\",\"level\":3,\"text\":\"When Payload might be for you\",\"anchor\":\"when-payload-might-be-for-you\"},{\"id\":\"when-payload-might-not-be-for-you\",\"level\":3,\"text\":\"When Payload might not be for you\",\"anchor\":\"when-payload-might-not-be-for-you\"}],\"keywords\":\"documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react\",\"label\":\"What is Payload?\",\"order\":10,\"title\":\"What is Payload?\"},{\"slug\":\"concepts\",\"content\":\"$35\",\"desc\":\"Payload is based around a small and intuitive set of concepts. Key concepts include collections, globals, fields and more.\",\"headings\":[{\"id\":\"config\",\"level\":2,\"text\":\"Config\",\"anchor\":\"config\"},{\"id\":\"database\",\"level\":2,\"text\":\"Database\",\"anchor\":\"database\"},{\"id\":\"collections\",\"level\":2,\"text\":\"Collections\",\"anchor\":\"collections\"},{\"id\":\"globals\",\"level\":2,\"text\":\"Globals\",\"anchor\":\"globals\"},{\"id\":\"fields\",\"level\":2,\"text\":\"Fields\",\"anchor\":\"fields\"},{\"id\":\"hooks\",\"level\":2,\"text\":\"Hooks\",\"anchor\":\"hooks\"},{\"id\":\"authentication\",\"level\":2,\"text\":\"Authentication\",\"anchor\":\"authentication\"},{\"id\":\"access-control\",\"level\":2,\"text\":\"Access Control\",\"anchor\":\"access-control\"},{\"id\":\"admin-panel\",\"level\":2,\"text\":\"Admin Panel\",\"anchor\":\"admin-panel\"},{\"id\":\"retrieving-data\",\"level\":2,\"text\":\"Retrieving Data\",\"anchor\":\"retrieving-data\"},{\"id\":\"local-api\",\"level\":3,\"text\":\"Local API\",\"anchor\":\"local-api\"},{\"id\":\"rest-api\",\"level\":3,\"text\":\"REST API\",\"anchor\":\"rest-api\"},{\"id\":\"graphql-api\",\"level\":3,\"text\":\"GraphQL API\",\"anchor\":\"graphql-api\"},{\"id\":\"package-structure\",\"level\":2,\"text\":\"Package Structure\",\"anchor\":\"package-structure\"}],\"keywords\":\"documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Concepts\",\"order\":20,\"title\":\"Payload Concepts\"},{\"slug\":\"installation\",\"content\":\"$36\",\"desc\":\"To quickly get started with Payload, simply run npx create-payload-app or install from scratch.\",\"headings\":[{\"id\":\"software-requirements\",\"level\":2,\"text\":\"Software Requirements\",\"anchor\":\"software-requirements\"},{\"id\":\"quickstart-with-create-payload-app\",\"level\":2,\"text\":\"Quickstart with create-payload-app\",\"anchor\":\"quickstart-with-create-payload-app\"},{\"id\":\"adding-to-an-existing-app\",\"level\":2,\"text\":\"Adding to an existing app\",\"anchor\":\"adding-to-an-existing-app\"}],\"keywords\":\"documentation, getting started, guide, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Installation\",\"order\":30,\"title\":\"Installation\"}]},{\"slug\":\"Configuration\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$37\",\"desc\":\"The Payload Config is central to everything that Payload does, from adding custom React components, to modifying collections, controlling localization and much more.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"typescript-config\",\"level\":3,\"text\":\"Typescript Config\",\"anchor\":\"typescript-config\"},{\"id\":\"config-location\",\"level\":2,\"text\":\"Config Location\",\"anchor\":\"config-location\"},{\"id\":\"customizing-the-config-location\",\"level\":3,\"text\":\"Customizing the Config Location\",\"anchor\":\"customizing-the-config-location\"},{\"id\":\"telemetry\",\"level\":2,\"text\":\"Telemetry\",\"anchor\":\"telemetry\"},{\"id\":\"cross-origin-resource-sharing-cors\",\"level\":2,\"text\":\"Cross-origin resource sharing (CORS)\",\"anchor\":\"cross-origin-resource-sharing-cors\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"},{\"id\":\"server-vs-client\",\"level\":2,\"text\":\"Server vs. Client\",\"anchor\":\"server-vs-client\"},{\"id\":\"compatibility-flags\",\"level\":2,\"text\":\"Compatibility flags\",\"anchor\":\"compatibility-flags\"}],\"keywords\":\"overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"The Payload Config\"},{\"slug\":\"collections\",\"content\":\"$38\",\"desc\":\"Structure your Collections for your needs by defining fields, adding slugs and labels, establishing access control, tying in hooks, setting timestamps and more.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"fields\",\"level\":3,\"text\":\"Fields\",\"anchor\":\"fields\"},{\"id\":\"access-control\",\"level\":3,\"text\":\"Access Control\",\"anchor\":\"access-control\"},{\"id\":\"hooks\",\"level\":3,\"text\":\"Hooks\",\"anchor\":\"hooks\"},{\"id\":\"admin-options\",\"level\":3,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"collections, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Collections\",\"order\":20,\"title\":\"Collection Configs\"},{\"slug\":\"globals\",\"content\":\"$39\",\"desc\":\"Set up your Global config for your needs by defining fields, adding slugs and labels, establishing access control, tying in hooks and more.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"fields\",\"level\":3,\"text\":\"Fields\",\"anchor\":\"fields\"},{\"id\":\"access-control\",\"level\":3,\"text\":\"Access Control\",\"anchor\":\"access-control\"},{\"id\":\"hooks\",\"level\":3,\"text\":\"Hooks\",\"anchor\":\"hooks\"},{\"id\":\"admin-options\",\"level\":3,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Globals\",\"order\":30,\"title\":\"Global Configs\"},{\"slug\":\"i18n\",\"content\":\"$3a\",\"desc\":\"Manage and customize internationalization support in your CMS editor experience\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"adding-languages\",\"level\":2,\"text\":\"Adding Languages\",\"anchor\":\"adding-languages\"},{\"id\":\"custom-translations\",\"level\":3,\"text\":\"Custom Translations\",\"anchor\":\"custom-translations\"},{\"id\":\"project-translations\",\"level\":3,\"text\":\"Project Translations\",\"anchor\":\"project-translations\"},{\"id\":\"node\",\"level\":2,\"text\":\"Node\",\"anchor\":\"node\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"I18n\",\"order\":40,\"title\":\"I18n\"},{\"slug\":\"localization\",\"content\":\"$3b\",\"desc\":\"Add and maintain as many locales as you need by adding Localization to your Payload Config, set options for default locale, fallbacks, fields and more.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"locales\",\"level\":3,\"text\":\"Locales\",\"anchor\":\"locales\"},{\"id\":\"field-localization\",\"level\":2,\"text\":\"Field Localization\",\"anchor\":\"field-localization\"},{\"id\":\"retrieving-localized-docs\",\"level\":2,\"text\":\"Retrieving Localized Docs\",\"anchor\":\"retrieving-localized-docs\"}],\"keywords\":\"localization, internationalization, i18n, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Localization\",\"order\":50,\"title\":\"Localization\"},{\"slug\":\"environment-vars\",\"content\":\"$3c\",\"desc\":\"Learn how to use Environment Variables in your Payload project\",\"headings\":[{\"id\":\"nextjs-applications\",\"level\":2,\"text\":\"Next.js Applications\",\"anchor\":\"nextjs-applications\"},{\"id\":\"client-side-environments\",\"level\":2,\"text\":\"Client-side Environments\",\"anchor\":\"client-side-environments\"},{\"id\":\"outside-of-nextjs\",\"level\":2,\"text\":\"Outside of Next.js\",\"anchor\":\"outside-of-nextjs\"}],\"keywords\":\"\",\"label\":\"Environment Variables\",\"order\":100,\"title\":\"Environment Variables\"}]},{\"slug\":\"Database\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$3d\",\"desc\":\"With Payload, you bring your own database and own your data. You have full control.\",\"headings\":[{\"id\":\"selecting-a-database\",\"level\":2,\"text\":\"Selecting a Database\",\"anchor\":\"selecting-a-database\"},{\"id\":\"non-relational-databases\",\"level\":3,\"text\":\"Non-Relational Databases\",\"anchor\":\"non-relational-databases\"},{\"id\":\"relational-databases\",\"level\":3,\"text\":\"Relational Databases\",\"anchor\":\"relational-databases\"},{\"id\":\"payload-differences\",\"level\":2,\"text\":\"Payload Differences\",\"anchor\":\"payload-differences\"}],\"keywords\":\"database, mongodb, postgres, documentation, Content Management System, cms, headless, typescript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Database\"},{\"slug\":\"migrations\",\"content\":\"$3e\",\"desc\":\"Payload features first-party database migrations all done in TypeScript.\",\"headings\":[{\"id\":\"migration-file-contents\",\"level\":2,\"text\":\"Migration file contents\",\"anchor\":\"migration-file-contents\"},{\"id\":\"using-transactions\",\"level\":2,\"text\":\"Using Transactions\",\"anchor\":\"using-transactions\"},{\"id\":\"migrations-directory\",\"level\":2,\"text\":\"Migrations Directory\",\"anchor\":\"migrations-directory\"},{\"id\":\"commands\",\"level\":2,\"text\":\"Commands\",\"anchor\":\"commands\"},{\"id\":\"migrate\",\"level\":3,\"text\":\"Migrate\",\"anchor\":\"migrate\"},{\"id\":\"create\",\"level\":3,\"text\":\"Create\",\"anchor\":\"create\"},{\"id\":\"status\",\"level\":3,\"text\":\"Status\",\"anchor\":\"status\"},{\"id\":\"down\",\"level\":3,\"text\":\"Down\",\"anchor\":\"down\"},{\"id\":\"refresh\",\"level\":3,\"text\":\"Refresh\",\"anchor\":\"refresh\"},{\"id\":\"reset\",\"level\":3,\"text\":\"Reset\",\"anchor\":\"reset\"},{\"id\":\"fresh\",\"level\":3,\"text\":\"Fresh\",\"anchor\":\"fresh\"},{\"id\":\"when-to-run-migrations\",\"level\":2,\"text\":\"When to run migrations\",\"anchor\":\"when-to-run-migrations\"},{\"id\":\"running-migrations-in-production\",\"level\":2,\"text\":\"Running migrations in production\",\"anchor\":\"running-migrations-in-production\"}],\"keywords\":\"database, migrations, ddl, sql, mongodb, postgres, documentation, Content Management System, cms, headless, typescript, node, react, nextjs\",\"label\":\"Migrations\",\"order\":20,\"title\":\"Migrations\"},{\"slug\":\"transactions\",\"content\":\"$3f\",\"desc\":\"Database transactions are fully supported within Payload.\",\"headings\":[{\"id\":\"async-hooks-with-transactions\",\"level\":2,\"text\":\"Async Hooks with Transactions\",\"anchor\":\"async-hooks-with-transactions\"},{\"id\":\"direct-transaction-access\",\"level\":2,\"text\":\"Direct Transaction Access\",\"anchor\":\"direct-transaction-access\"},{\"id\":\"disabling-transactions\",\"level\":2,\"text\":\"Disabling Transactions\",\"anchor\":\"disabling-transactions\"}],\"keywords\":\"database, transactions, sql, mongodb, postgres, documentation, Content Management System, cms, headless, typescript, node, react, nextjs\",\"label\":\"Transactions\",\"order\":30,\"title\":\"Transactions\"},{\"slug\":\"mongodb\",\"content\":\"$40\",\"desc\":\"Payload has supported MongoDB natively since we started. The flexible nature of MongoDB lends itself well to Payload's powerful fields.\",\"headings\":[{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"access-to-mongoose-models\",\"level\":2,\"text\":\"Access to Mongoose models\",\"anchor\":\"access-to-mongoose-models\"}],\"keywords\":\"MongoDB, documentation, typescript, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"MongoDB\",\"order\":40,\"title\":\"MongoDB\"},{\"slug\":\"postgres\",\"content\":\"$41\",\"desc\":\"Payload supports Postgres through an officially supported Drizzle Database Adapter.\",\"headings\":[{\"id\":\"usage\",\"level\":3,\"text\":\"Usage\",\"anchor\":\"usage\"},{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"access-to-drizzle\",\"level\":2,\"text\":\"Access to Drizzle\",\"anchor\":\"access-to-drizzle\"},{\"id\":\"tables-relations-and-enums\",\"level\":2,\"text\":\"Tables, relations, and enums\",\"anchor\":\"tables-relations-and-enums\"},{\"id\":\"prototyping-in-development-mode\",\"level\":2,\"text\":\"Prototyping in development mode\",\"anchor\":\"prototyping-in-development-mode\"},{\"id\":\"migration-workflows\",\"level\":2,\"text\":\"Migration workflows\",\"anchor\":\"migration-workflows\"},{\"id\":\"drizzle-schema-hooks\",\"level\":2,\"text\":\"Drizzle schema hooks\",\"anchor\":\"drizzle-schema-hooks\"},{\"id\":\"beforeschemainit\",\"level\":3,\"text\":\"beforeSchemaInit\",\"anchor\":\"beforeschemainit\"},{\"id\":\"afterschemainit\",\"level\":3,\"text\":\"afterSchemaInit\",\"anchor\":\"afterschemainit\"}],\"keywords\":\"Postgres, documentation, typescript, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Postgres\",\"order\":50,\"title\":\"Postgres\"},{\"slug\":\"sqlite\",\"content\":\"$42\",\"desc\":\"Payload supports SQLite through an officially supported Drizzle Database Adapter.\",\"headings\":[{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"access-to-drizzle\",\"level\":2,\"text\":\"Access to Drizzle\",\"anchor\":\"access-to-drizzle\"},{\"id\":\"tables-and-relations\",\"level\":2,\"text\":\"Tables and relations\",\"anchor\":\"tables-and-relations\"},{\"id\":\"prototyping-in-development-mode\",\"level\":2,\"text\":\"Prototyping in development mode\",\"anchor\":\"prototyping-in-development-mode\"},{\"id\":\"migration-workflows\",\"level\":2,\"text\":\"Migration workflows\",\"anchor\":\"migration-workflows\"},{\"id\":\"drizzle-schema-hooks\",\"level\":2,\"text\":\"Drizzle schema hooks\",\"anchor\":\"drizzle-schema-hooks\"},{\"id\":\"beforeschemainit\",\"level\":3,\"text\":\"beforeSchemaInit\",\"anchor\":\"beforeschemainit\"},{\"id\":\"afterschemainit\",\"level\":3,\"text\":\"afterSchemaInit\",\"anchor\":\"afterschemainit\"}],\"keywords\":\"SQLite, documentation, typescript, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"SQLite\",\"order\":60,\"title\":\"SQLite\"}]},{\"slug\":\"Fields\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$43\",\"desc\":\"Fields are the building blocks of Payload, find out how to add or remove a field, change field type, add hooks, define Access Control and Validation.\",\"headings\":[{\"id\":\"field-types\",\"level\":2,\"text\":\"Field Types\",\"anchor\":\"field-types\"},{\"id\":\"data-fields\",\"level\":3,\"text\":\"Data Fields\",\"anchor\":\"data-fields\"},{\"id\":\"presentational-fields\",\"level\":3,\"text\":\"Presentational Fields\",\"anchor\":\"presentational-fields\"},{\"id\":\"field-options\",\"level\":2,\"text\":\"Field Options\",\"anchor\":\"field-options\"},{\"id\":\"field-names\",\"level\":3,\"text\":\"Field Names\",\"anchor\":\"field-names\"},{\"id\":\"field-level-hooks\",\"level\":3,\"text\":\"Field-level Hooks\",\"anchor\":\"field-level-hooks\"},{\"id\":\"field-level-access-control\",\"level\":3,\"text\":\"Field-level Access Control\",\"anchor\":\"field-level-access-control\"},{\"id\":\"default-values\",\"level\":3,\"text\":\"Default Values\",\"anchor\":\"default-values\"},{\"id\":\"validation\",\"level\":3,\"text\":\"Validation\",\"anchor\":\"validation\"},{\"id\":\"admin-options\",\"level\":3,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"custom-id-fields\",\"level\":2,\"text\":\"Custom ID Fields\",\"anchor\":\"custom-id-fields\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"overview, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Fields Overview\"},{\"slug\":\"array\",\"content\":\"$44\",\"desc\":\"Array Fields are intended for sets of repeating fields, that you define. Learn how to use Array Fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"example-of-a-custom-rowlabel-component\",\"level\":3,\"text\":\"Example of a custom RowLabel component\",\"anchor\":\"example-of-a-custom-rowlabel-component\"}],\"keywords\":\"array, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Array\",\"order\":20,\"title\":\"Array Field\"},{\"slug\":\"blocks\",\"content\":\"$45\",\"desc\":\"The Blocks Field is a great layout build and can be used to construct any flexible content model. Learn how to use Block Fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"block-configs\",\"level\":2,\"text\":\"Block Configs\",\"anchor\":\"block-configs\"},{\"id\":\"auto-generated-data-per-block\",\"level\":3,\"text\":\"Auto-generated data per block\",\"anchor\":\"auto-generated-data-per-block\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"blocks, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Blocks\",\"order\":30,\"title\":\"Blocks Field\"},{\"slug\":\"checkbox\",\"content\":\"$46\",\"desc\":\"Checkbox field types allow the developer to save a boolean value in the database. Learn how to use Checkbox fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"checkbox, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Checkbox\",\"order\":40,\"title\":\"Checkbox Field\"},{\"slug\":\"code\",\"content\":\"$47\",\"desc\":\"The Code field type will store any string in the Database. Learn how to use Code fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"code, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Code\",\"order\":50,\"title\":\"Code Field\"},{\"slug\":\"json\",\"content\":\"$48\",\"desc\":\"The JSON field type will store any string in the Database. Learn how to use JSON fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"json-schema-validation\",\"level\":2,\"text\":\"JSON Schema Validation\",\"anchor\":\"json-schema-validation\"},{\"id\":\"local-json-schema\",\"level\":3,\"text\":\"Local JSON Schema\",\"anchor\":\"local-json-schema\"},{\"id\":\"remote-json-schema\",\"level\":3,\"text\":\"Remote JSON Schema\",\"anchor\":\"remote-json-schema\"}],\"keywords\":\"json, jsonSchema, schema, validation, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"JSON\",\"order\":50,\"title\":\"JSON Field\"},{\"slug\":\"collapsible\",\"content\":\"$49\",\"desc\":\"With the Collapsible field, you can place fields within a collapsible layout component that can be collapsed / expanded.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"row, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Collapsible\",\"order\":60,\"title\":\"Collapsible Field\"},{\"slug\":\"date\",\"content\":\"$4a\",\"desc\":\"The Date field type stores a Date in the database. Learn how to use and customize the Date field, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"display-format-and-picker-appearance\",\"level\":3,\"text\":\"Display Format and Picker Appearance\",\"anchor\":\"display-format-and-picker-appearance\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"date, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Date\",\"order\":70,\"title\":\"Date Field\"},{\"slug\":\"email\",\"content\":\"$4b\",\"desc\":\"The Email field enforces that the value provided is a valid email address. Learn how to use Email fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"email, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Email\",\"order\":80,\"title\":\"Email Field\"},{\"slug\":\"group\",\"content\":\"$4c\",\"desc\":\"The Group field allows other fields to be nested under a common property. Learn how to use Group fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"group, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Group\",\"order\":90,\"title\":\"Group Field\"},{\"slug\":\"number\",\"content\":\"$4d\",\"desc\":\"Number fields store and validate numeric data. Learn how to use and format Number fields, see examples and Number field options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"number, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Number\",\"order\":100,\"title\":\"Number Field\"},{\"slug\":\"point\",\"content\":\"$4e\",\"desc\":\"The Point field type stores coordinates in the database. Learn how to use Point field for geolocation and geometry.\",\"headings\":[{\"id\":\"config\",\"level\":2,\"text\":\"Config\",\"anchor\":\"config\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"querying-near\",\"level\":2,\"text\":\"Querying - near\",\"anchor\":\"querying-near\"},{\"id\":\"querying-within\",\"level\":2,\"text\":\"Querying - within\",\"anchor\":\"querying-within\"},{\"id\":\"querying-intersects\",\"level\":2,\"text\":\"Querying - intersects\",\"anchor\":\"querying-intersects\"}],\"keywords\":\"point, geolocation, geospatial, geojson, 2dsphere, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Point\",\"order\":110,\"title\":\"Point Field\"},{\"slug\":\"radio\",\"content\":\"$4f\",\"desc\":\"The Radio field type allows for the selection of one value from a predefined set of possible values. Learn how to use Radio fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"radio, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Radio Group\",\"order\":120,\"title\":\"Radio Group Field\"},{\"slug\":\"relationship\",\"content\":\"$50\",\"desc\":\"The Relationship field provides the ability to relate documents together. Learn how to use Relationship fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"sort-options\",\"level\":3,\"text\":\"Sort Options\",\"anchor\":\"sort-options\"},{\"id\":\"filtering-relationship-options\",\"level\":2,\"text\":\"Filtering relationship options\",\"anchor\":\"filtering-relationship-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"bi-directional-relationships\",\"level\":2,\"text\":\"Bi-directional relationships\",\"anchor\":\"bi-directional-relationships\"},{\"id\":\"how-the-data-is-saved\",\"level\":2,\"text\":\"How the data is saved\",\"anchor\":\"how-the-data-is-saved\"},{\"id\":\"has-one\",\"level\":3,\"text\":\"Has One\",\"anchor\":\"has-one\"},{\"id\":\"has-one-polymorphic\",\"level\":3,\"text\":\"Has One - Polymorphic\",\"anchor\":\"has-one-polymorphic\"},{\"id\":\"has-many\",\"level\":3,\"text\":\"Has Many\",\"anchor\":\"has-many\"},{\"id\":\"has-many-polymorphic\",\"level\":3,\"text\":\"Has Many - Polymorphic\",\"anchor\":\"has-many-polymorphic\"},{\"id\":\"querying-and-filtering-polymorphic-relationships\",\"level\":3,\"text\":\"Querying and Filtering Polymorphic Relationships\",\"anchor\":\"querying-and-filtering-polymorphic-relationships\"}],\"keywords\":\"relationship, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Relationship\",\"order\":130,\"title\":\"Relationship Field\"},{\"slug\":\"join\",\"content\":\"$51\",\"desc\":\"The Join field provides the ability to work on related documents. Learn how to use Join field, see examples and options.\",\"headings\":[{\"id\":\"schema-advice\",\"level\":3,\"text\":\"Schema advice\",\"anchor\":\"schema-advice\"},{\"id\":\"using-the-join-field-to-have-full-control-of-your-database-schema\",\"level\":3,\"text\":\"Using the Join field to have full control of your database schema\",\"anchor\":\"using-the-join-field-to-have-full-control-of-your-database-schema\"},{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-config-options\",\"level\":2,\"text\":\"Admin Config Options\",\"anchor\":\"admin-config-options\"},{\"id\":\"join-field-data\",\"level\":2,\"text\":\"Join Field Data\",\"anchor\":\"join-field-data\"},{\"id\":\"query-options\",\"level\":2,\"text\":\"Query Options\",\"anchor\":\"query-options\"},{\"id\":\"local-api\",\"level\":3,\"text\":\"Local API\",\"anchor\":\"local-api\"},{\"id\":\"rest-api\",\"level\":3,\"text\":\"Rest API\",\"anchor\":\"rest-api\"},{\"id\":\"graphql\",\"level\":3,\"text\":\"GraphQL\",\"anchor\":\"graphql\"}],\"keywords\":\"join, relationship, junction, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Join\",\"order\":140,\"title\":\"Join Field\"},{\"slug\":\"rich-text\",\"content\":\"$52\",\"desc\":\"The Rich Text field allows dynamic content to be written through the Admin Panel. Learn how to use Rich Text fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"editor-specific-options\",\"level\":2,\"text\":\"Editor-specific Options\",\"anchor\":\"editor-specific-options\"}],\"keywords\":\"rich text, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Rich Text\",\"order\":140,\"title\":\"Rich Text Field\"},{\"slug\":\"row\",\"content\":\"$53\",\"desc\":\"With the Row field you can arrange fields next to each other in the Admin Panel to help you customize your Dashboard.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"row, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Row\",\"order\":150,\"title\":\"Row Field\"},{\"slug\":\"select\",\"content\":\"$54\",\"desc\":\"The Select field provides a dropdown-style interface for choosing options from a predefined list. Learn how to use Select fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"customization\",\"level\":2,\"text\":\"Customization\",\"anchor\":\"customization\"}],\"keywords\":\"select, multi-select, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Select\",\"order\":160,\"title\":\"Select Field\"},{\"slug\":\"tabs\",\"content\":\"$55\",\"desc\":\"The Tabs field is a great way to organize complex editing experiences into specific tab-based areas.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"tab-specific-config\",\"level\":3,\"text\":\"Tab-specific Config\",\"anchor\":\"tab-specific-config\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"tabs, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Tabs\",\"order\":170,\"title\":\"Tabs Field\"},{\"slug\":\"text\",\"content\":\"$56\",\"desc\":\"Text field types simply save a string to the database and provide the Admin Panel with a text input. Learn how to use Text fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"text, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Text\",\"order\":180,\"title\":\"Text Field\"},{\"slug\":\"textarea\",\"content\":\"$57\",\"desc\":\"Textarea field types save a string to the database, similar to the Text field type but equipped for longer text. Learn how to use Textarea fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"textarea, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Textarea\",\"order\":190,\"title\":\"Textarea Field\"},{\"slug\":\"ui\",\"content\":\"$58\",\"desc\":\"UI fields are purely presentational and allow developers to customize the Admin Panel to a very fine degree, including adding actions and other functions.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"custom field, react component, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"UI\",\"order\":200,\"title\":\"UI Field\"},{\"slug\":\"upload\",\"content\":\"$59\",\"desc\":\"Upload fields will allow a file to be uploaded, only from a collection supporting Uploads. Learn how to use Upload fields, see examples and options.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"filtering-upload-options\",\"level\":2,\"text\":\"Filtering upload options\",\"anchor\":\"filtering-upload-options\"},{\"id\":\"filter-options-example\",\"level\":3,\"text\":\"Example\",\"anchor\":\"filter-options-example\"},{\"id\":\"bi-directional-relationships\",\"level\":2,\"text\":\"Bi-directional relationships\",\"anchor\":\"bi-directional-relationships\"}],\"keywords\":\"upload, images media, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Upload\",\"order\":210,\"title\":\"Upload Field\"}]},{\"slug\":\"Access-Control\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$5a\",\"desc\":\"Payload makes it simple to define and manage Access Control. By declaring roles, you can set permissions and restrict what your users can interact with.\",\"headings\":[{\"id\":\"default-access-control\",\"level\":2,\"text\":\"Default Access Control\",\"anchor\":\"default-access-control\"},{\"id\":\"the-access-operation\",\"level\":2,\"text\":\"The Access Operation\",\"anchor\":\"the-access-operation\"}],\"keywords\":\"overview, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Access Control\"},{\"slug\":\"collections\",\"content\":\"$5b\",\"desc\":\"With Collection-level Access Control you can define which users can create, read, update or delete Collections.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"create\",\"level\":3,\"text\":\"Create\",\"anchor\":\"create\"},{\"id\":\"read\",\"level\":3,\"text\":\"Read\",\"anchor\":\"read\"},{\"id\":\"update\",\"level\":3,\"text\":\"Update\",\"anchor\":\"update\"},{\"id\":\"delete\",\"level\":3,\"text\":\"Delete\",\"anchor\":\"delete\"},{\"id\":\"admin\",\"level\":3,\"text\":\"Admin\",\"anchor\":\"admin\"},{\"id\":\"unlock\",\"level\":3,\"text\":\"Unlock\",\"anchor\":\"unlock\"},{\"id\":\"read-versions\",\"level\":3,\"text\":\"Read Versions\",\"anchor\":\"read-versions\"}],\"keywords\":\"collections, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Collections\",\"order\":20,\"title\":\"Collection Access Control\"},{\"slug\":\"globals\",\"content\":\"$5c\",\"desc\":\"Global-level Access Control is specified within each Global's `access` property and allows you to define which users can read or update Globals.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"read\",\"level\":3,\"text\":\"Read\",\"anchor\":\"read\"},{\"id\":\"update\",\"level\":3,\"text\":\"Update\",\"anchor\":\"update\"},{\"id\":\"read-versions\",\"level\":3,\"text\":\"Read Versions\",\"anchor\":\"read-versions\"}],\"keywords\":\"globals, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Globals\",\"order\":30,\"title\":\"Globals Access Control\"},{\"slug\":\"fields\",\"content\":\"$5d\",\"desc\":\"Field-level Access Control is specified within a field's config, and allows you to define which users can create, read or update Fields.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"create\",\"level\":3,\"text\":\"Create\",\"anchor\":\"create\"},{\"id\":\"read\",\"level\":3,\"text\":\"Read\",\"anchor\":\"read\"},{\"id\":\"update\",\"level\":3,\"text\":\"Update\",\"anchor\":\"update\"}],\"keywords\":\"fields, access control, permissions, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Fields\",\"order\":40,\"title\":\"Field-level Access Control\"}]},{\"slug\":\"Hooks\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$5e\",\"desc\":\"Hooks allow you to add your own logic to Payload, including integrating with third-party APIs, adding auto-generated data, or modifying Payload's base functionality.\",\"headings\":[{\"id\":\"root-hooks\",\"level\":2,\"text\":\"Root Hooks\",\"anchor\":\"root-hooks\"},{\"id\":\"aftererror\",\"level\":3,\"text\":\"afterError\",\"anchor\":\"aftererror\"},{\"id\":\"async-vs-synchronous\",\"level\":2,\"text\":\"Async vs. Synchronous\",\"anchor\":\"async-vs-synchronous\"},{\"id\":\"server-only-execution\",\"level\":2,\"text\":\"Server-only Execution\",\"anchor\":\"server-only-execution\"}],\"keywords\":\"hooks, overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Hooks Overview\"},{\"slug\":\"collections\",\"content\":\"$5f\",\"desc\":\"You can add hooks to any Collection, several hook types are available including beforeChange, afterRead, afterDelete and more.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"beforeoperation\",\"level\":3,\"text\":\"beforeOperation\",\"anchor\":\"beforeoperation\"},{\"id\":\"beforevalidate\",\"level\":3,\"text\":\"beforeValidate\",\"anchor\":\"beforevalidate\"},{\"id\":\"beforechange\",\"level\":3,\"text\":\"beforeChange\",\"anchor\":\"beforechange\"},{\"id\":\"afterchange\",\"level\":3,\"text\":\"afterChange\",\"anchor\":\"afterchange\"},{\"id\":\"beforeread\",\"level\":3,\"text\":\"beforeRead\",\"anchor\":\"beforeread\"},{\"id\":\"afterread\",\"level\":3,\"text\":\"afterRead\",\"anchor\":\"afterread\"},{\"id\":\"beforedelete\",\"level\":3,\"text\":\"beforeDelete\",\"anchor\":\"beforedelete\"},{\"id\":\"afterdelete\",\"level\":3,\"text\":\"afterDelete\",\"anchor\":\"afterdelete\"},{\"id\":\"afteroperation\",\"level\":3,\"text\":\"afterOperation\",\"anchor\":\"afteroperation\"},{\"id\":\"aftererror\",\"level\":3,\"text\":\"afterError\",\"anchor\":\"aftererror\"},{\"id\":\"beforelogin\",\"level\":3,\"text\":\"beforeLogin\",\"anchor\":\"beforelogin\"},{\"id\":\"afterlogin\",\"level\":3,\"text\":\"afterLogin\",\"anchor\":\"afterlogin\"},{\"id\":\"afterlogout\",\"level\":3,\"text\":\"afterLogout\",\"anchor\":\"afterlogout\"},{\"id\":\"afterme\",\"level\":3,\"text\":\"afterMe\",\"anchor\":\"afterme\"},{\"id\":\"afterrefresh\",\"level\":3,\"text\":\"afterRefresh\",\"anchor\":\"afterrefresh\"},{\"id\":\"afterforgotpassword\",\"level\":3,\"text\":\"afterForgotPassword\",\"anchor\":\"afterforgotpassword\"},{\"id\":\"refresh\",\"level\":3,\"text\":\"refresh\",\"anchor\":\"refresh\"},{\"id\":\"me\",\"level\":3,\"text\":\"me\",\"anchor\":\"me\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"hooks, collections, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Collections\",\"order\":20,\"title\":\"Collection Hooks\"},{\"slug\":\"globals\",\"content\":\"$60\",\"desc\":\"Hooks can be added to any Global and allow you to validate data, flatten locales, hide protected fields, remove fields and more.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"beforevalidate\",\"level\":3,\"text\":\"beforeValidate\",\"anchor\":\"beforevalidate\"},{\"id\":\"beforechange\",\"level\":3,\"text\":\"beforeChange\",\"anchor\":\"beforechange\"},{\"id\":\"afterchange\",\"level\":3,\"text\":\"afterChange\",\"anchor\":\"afterchange\"},{\"id\":\"beforeread\",\"level\":3,\"text\":\"beforeRead\",\"anchor\":\"beforeread\"},{\"id\":\"afterread\",\"level\":3,\"text\":\"afterRead\",\"anchor\":\"afterread\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"hooks, globals, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Globals\",\"order\":30,\"title\":\"Global Hooks\"},{\"slug\":\"fields\",\"content\":\"$61\",\"desc\":\"Hooks can be added to any fields, and optionally modify the return value of the field before the operation continues.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"beforevalidate\",\"level\":3,\"text\":\"beforeValidate\",\"anchor\":\"beforevalidate\"},{\"id\":\"beforechange\",\"level\":3,\"text\":\"beforeChange\",\"anchor\":\"beforechange\"},{\"id\":\"afterchange\",\"level\":3,\"text\":\"afterChange\",\"anchor\":\"afterchange\"},{\"id\":\"afterread\",\"level\":3,\"text\":\"afterRead\",\"anchor\":\"afterread\"},{\"id\":\"beforeduplicate\",\"level\":3,\"text\":\"beforeDuplicate\",\"anchor\":\"beforeduplicate\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"hooks, fields, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Fields\",\"order\":40,\"title\":\"Field Hooks\"},{\"slug\":\"context\",\"content\":\"$62\",\"desc\":\"Context allows you to pass in extra data that can be shared between hooks\",\"headings\":[{\"id\":\"when-to-use-context\",\"level\":2,\"text\":\"When To Use Context\",\"anchor\":\"when-to-use-context\"},{\"id\":\"how-to-use-context\",\"level\":2,\"text\":\"How To Use Context\",\"anchor\":\"how-to-use-context\"},{\"id\":\"passing-data-between-hooks\",\"level\":3,\"text\":\"Passing Data Between Hooks\",\"anchor\":\"passing-data-between-hooks\"},{\"id\":\"preventing-infinite-loops\",\"level\":3,\"text\":\"Preventing Infinite Loops\",\"anchor\":\"preventing-infinite-loops\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"hooks, context, payload context, payloadcontext, data, extra data, shared data, shared, extra\",\"label\":\"Context\",\"order\":50,\"title\":\"Context\"}]}]},{\"groupLabel\":\"Managing Data\",\"topics\":[{\"slug\":\"Local-API\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$63\",\"desc\":\"The Payload Local API allows you to interact with your database and execute the same operations that are available through REST and GraphQL within Node, directly on your server.\",\"headings\":[{\"id\":\"accessing-payload\",\"level\":2,\"text\":\"Accessing Payload\",\"anchor\":\"accessing-payload\"},{\"id\":\"local-options-available\",\"level\":2,\"text\":\"Local options available\",\"anchor\":\"local-options-available\"},{\"id\":\"transactions\",\"level\":2,\"text\":\"Transactions\",\"anchor\":\"transactions\"},{\"id\":\"collections\",\"level\":2,\"text\":\"Collections\",\"anchor\":\"collections\"},{\"id\":\"collection-create\",\"level\":3,\"text\":\"Create\",\"anchor\":\"collection-create\"},{\"id\":\"collection-find\",\"level\":3,\"text\":\"Find\",\"anchor\":\"collection-find\"},{\"id\":\"collection-find-by-id\",\"level\":3,\"text\":\"Find by ID\",\"anchor\":\"collection-find-by-id\"},{\"id\":\"collection-count\",\"level\":3,\"text\":\"Count\",\"anchor\":\"collection-count\"},{\"id\":\"collection-update-by-id\",\"level\":3,\"text\":\"Update by ID\",\"anchor\":\"collection-update-by-id\"},{\"id\":\"collection-update-many\",\"level\":3,\"text\":\"Update Many\",\"anchor\":\"collection-update-many\"},{\"id\":\"collection-delete\",\"level\":3,\"text\":\"Delete\",\"anchor\":\"collection-delete\"},{\"id\":\"collection-delete-many\",\"level\":3,\"text\":\"Delete Many\",\"anchor\":\"collection-delete-many\"},{\"id\":\"auth-operations\",\"level\":2,\"text\":\"Auth Operations\",\"anchor\":\"auth-operations\"},{\"id\":\"login\",\"level\":3,\"text\":\"Login\",\"anchor\":\"login\"},{\"id\":\"forgot-password\",\"level\":3,\"text\":\"Forgot Password\",\"anchor\":\"forgot-password\"},{\"id\":\"reset-password\",\"level\":3,\"text\":\"Reset Password\",\"anchor\":\"reset-password\"},{\"id\":\"unlock\",\"level\":3,\"text\":\"Unlock\",\"anchor\":\"unlock\"},{\"id\":\"verify\",\"level\":3,\"text\":\"Verify\",\"anchor\":\"verify\"},{\"id\":\"globals\",\"level\":2,\"text\":\"Globals\",\"anchor\":\"globals\"},{\"id\":\"global-find\",\"level\":3,\"text\":\"Find\",\"anchor\":\"global-find\"},{\"id\":\"global-update\",\"level\":3,\"text\":\"Update\",\"anchor\":\"global-update\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"local api, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Local API\"},{\"slug\":\"outside-nextjs\",\"content\":\"$64\",\"desc\":\"Payload can be used outside of Next.js within standalone scripts or in other frameworks like Remix, SvelteKit, Nuxt, and similar.\",\"headings\":[{\"id\":\"importing-the-payload-config-outside-of-nextjs\",\"level\":2,\"text\":\"Importing the Payload Config outside of Next.js\",\"anchor\":\"importing-the-payload-config-outside-of-nextjs\"},{\"id\":\"troubleshooting\",\"level\":3,\"text\":\"Troubleshooting\",\"anchor\":\"troubleshooting\"}],\"keywords\":\"local api, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, express\",\"label\":\"Outside Next.js\",\"order\":20,\"title\":\"Using Payload outside Next.js\"}]},{\"slug\":\"REST-API\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$65\",\"desc\":\"Payload generates a fully functional REST API from your Collection and Global configs.\",\"headings\":[{\"id\":\"collections\",\"level\":2,\"text\":\"Collections\",\"anchor\":\"collections\"},{\"id\":\"auth-operations\",\"level\":2,\"text\":\"Auth Operations\",\"anchor\":\"auth-operations\"},{\"id\":\"globals\",\"level\":2,\"text\":\"Globals\",\"anchor\":\"globals\"},{\"id\":\"preferences\",\"level\":2,\"text\":\"Preferences\",\"anchor\":\"preferences\"},{\"id\":\"custom-endpoints\",\"level\":2,\"text\":\"Custom Endpoints\",\"anchor\":\"custom-endpoints\"},{\"id\":\"method-override-for-get-requests\",\"level\":2,\"text\":\"Method Override for GET Requests\",\"anchor\":\"method-override-for-get-requests\"},{\"id\":\"how-to-use\",\"level\":3,\"text\":\"How to Use\",\"anchor\":\"how-to-use\"},{\"id\":\"example\",\"level\":3,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"rest, api, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"REST API\"}]},{\"slug\":\"GraphQL\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$66\",\"desc\":\"Payload ships with a fully featured and extensible GraphQL API, which can be used in addition to the REST and Local APIs to give you more flexibility.\",\"headings\":[{\"id\":\"graphql-options\",\"level\":2,\"text\":\"GraphQL Options\",\"anchor\":\"graphql-options\"},{\"id\":\"collections\",\"level\":2,\"text\":\"Collections\",\"anchor\":\"collections\"},{\"id\":\"globals\",\"level\":2,\"text\":\"Globals\",\"anchor\":\"globals\"},{\"id\":\"preferences\",\"level\":2,\"text\":\"Preferences\",\"anchor\":\"preferences\"},{\"id\":\"graphql-playground\",\"level\":2,\"text\":\"GraphQL Playground\",\"anchor\":\"graphql-playground\"},{\"id\":\"query-complexity-limits\",\"level\":2,\"text\":\"Query complexity limits\",\"anchor\":\"query-complexity-limits\"}],\"keywords\":\"graphql, resolvers, mutations, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"GraphQL Overview\"},{\"slug\":\"extending\",\"content\":\"$67\",\"desc\":\"Payload allows you to add your own GraphQL queries and mutations, simply set up GraphQL in your main Payload Config by following these instructions.\",\"headings\":[{\"id\":\"return-value\",\"level\":2,\"text\":\"Return value\",\"anchor\":\"return-value\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"resolver-function\",\"level\":2,\"text\":\"Resolver function\",\"anchor\":\"resolver-function\"},{\"id\":\"types\",\"level\":2,\"text\":\"Types\",\"anchor\":\"types\"},{\"id\":\"best-practices\",\"level\":2,\"text\":\"Best practices\",\"anchor\":\"best-practices\"}],\"keywords\":\"graphql, resolvers, mutations, custom, queries, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Custom Queries and Mutations\",\"order\":20,\"title\":\"Adding your own Queries and Mutations\"},{\"slug\":\"graphql-schema\",\"content\":\"$68\",\"desc\":\"Output your own GraphQL schema based on your collections and globals to a file.\",\"headings\":[{\"id\":\"schema-generation-script\",\"level\":2,\"text\":\"Schema generation script\",\"anchor\":\"schema-generation-script\"},{\"id\":\"custom-field-schemas\",\"level\":2,\"text\":\"Custom Field Schemas\",\"anchor\":\"custom-field-schemas\"},{\"id\":\"adding-an-npm-script\",\"level\":3,\"text\":\"Adding an NPM script\",\"anchor\":\"adding-an-npm-script\"}],\"keywords\":\"headless cms, typescript, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"GraphQL Schema\",\"order\":30,\"title\":\"GraphQL Schema\"}]},{\"slug\":\"Queries\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$69\",\"desc\":\"Payload provides a querying language through all APIs, allowing you to filter or search through documents within a Collection.\",\"headings\":[{\"id\":\"operators\",\"level\":2,\"text\":\"Operators\",\"anchor\":\"operators\"},{\"id\":\"and-or-logic\",\"level\":3,\"text\":\"And / Or Logic\",\"anchor\":\"and-or-logic\"},{\"id\":\"nested-properties\",\"level\":3,\"text\":\"Nested properties\",\"anchor\":\"nested-properties\"},{\"id\":\"writing-queries\",\"level\":2,\"text\":\"Writing Queries\",\"anchor\":\"writing-queries\"},{\"id\":\"local-api\",\"level\":3,\"text\":\"Local API\",\"anchor\":\"local-api\"},{\"id\":\"graphql-api\",\"level\":3,\"text\":\"GraphQL API\",\"anchor\":\"graphql-api\"},{\"id\":\"rest-api\",\"level\":3,\"text\":\"REST API\",\"anchor\":\"rest-api\"}],\"keywords\":\"query, documents, overview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Querying your Documents\"},{\"slug\":\"sort\",\"content\":\"$6a\",\"desc\":\"Payload sort allows you to order your documents by a field in ascending or descending order.\",\"headings\":[{\"id\":\"local-api\",\"level\":2,\"text\":\"Local API\",\"anchor\":\"local-api\"},{\"id\":\"rest-api\",\"level\":2,\"text\":\"REST API\",\"anchor\":\"rest-api\"},{\"id\":\"graphql-api\",\"level\":2,\"text\":\"GraphQL API\",\"anchor\":\"graphql-api\"}],\"keywords\":\"query, documents, pagination, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Sort\",\"order\":20,\"title\":\"Sort\"},{\"slug\":\"depth\",\"content\":\"$6b\",\"desc\":\"Payload depth determines how many levels down related documents should be automatically populated when retrieved.\",\"headings\":[{\"id\":\"local-api\",\"level\":2,\"text\":\"Local API\",\"anchor\":\"local-api\"},{\"id\":\"rest-api\",\"level\":2,\"text\":\"REST API\",\"anchor\":\"rest-api\"},{\"id\":\"max-depth\",\"level\":2,\"text\":\"Max Depth\",\"anchor\":\"max-depth\"}],\"keywords\":\"query, documents, pagination, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Depth\",\"order\":30,\"title\":\"Depth\"},{\"slug\":\"select\",\"content\":\"$6c\",\"desc\":\"Payload select determines which fields are selected to the result.\",\"headings\":[{\"id\":\"local-api\",\"level\":2,\"text\":\"Local API\",\"anchor\":\"local-api\"},{\"id\":\"rest-api\",\"level\":2,\"text\":\"REST API\",\"anchor\":\"rest-api\"},{\"id\":\"defaultpopulate-collection-config-property\",\"level\":2,\"text\":\"defaultPopulate collection config property\",\"anchor\":\"defaultpopulate-collection-config-property\"},{\"id\":\"populate\",\"level\":2,\"text\":\"populate\",\"anchor\":\"populate\"}],\"keywords\":\"query, documents, pagination, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Select\",\"order\":30,\"title\":\"Select\"},{\"slug\":\"pagination\",\"content\":\"$6d\",\"desc\":\"Payload queries are equipped with automatic pagination so you create paginated lists of documents within your app.\",\"headings\":[{\"id\":\"pagination-controls\",\"level\":2,\"text\":\"Pagination controls\",\"anchor\":\"pagination-controls\"},{\"id\":\"disabling-pagination-within-local-api\",\"level\":3,\"text\":\"Disabling pagination within Local API\",\"anchor\":\"disabling-pagination-within-local-api\"}],\"keywords\":\"query, documents, pagination, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Pagination\",\"order\":40,\"title\":\"Pagination\"}]}]},{\"groupLabel\":\"Features\",\"topics\":[{\"slug\":\"Admin\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$6e\",\"desc\":\"Manage your data and customize the Payload Admin Panel by swapping in your own React components. Create, modify or remove views, fields, styles and much more.\",\"headings\":[{\"id\":\"project-structure\",\"level\":2,\"text\":\"Project Structure\",\"anchor\":\"project-structure\"},{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"the-admin-user-collection\",\"level\":3,\"text\":\"The Admin User Collection\",\"anchor\":\"the-admin-user-collection\"},{\"id\":\"role-based-access-control\",\"level\":3,\"text\":\"Role-based Access Control\",\"anchor\":\"role-based-access-control\"},{\"id\":\"customizing-routes\",\"level\":2,\"text\":\"Customizing Routes\",\"anchor\":\"customizing-routes\"},{\"id\":\"root-level-routes\",\"level\":3,\"text\":\"Root-level Routes\",\"anchor\":\"root-level-routes\"},{\"id\":\"admin-level-routes\",\"level\":3,\"text\":\"Admin-level Routes\",\"anchor\":\"admin-level-routes\"},{\"id\":\"i18n\",\"level\":2,\"text\":\"I18n\",\"anchor\":\"i18n\"},{\"id\":\"light-and-dark-modes\",\"level\":2,\"text\":\"Light and Dark Modes\",\"anchor\":\"light-and-dark-modes\"}],\"keywords\":\"admin, components, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"The Admin Panel\"},{\"slug\":\"collections\",\"content\":\"$6f\",\"desc\":\"\",\"headings\":[{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"custom-components\",\"level\":3,\"text\":\"Custom Components\",\"anchor\":\"custom-components\"},{\"id\":\"preview\",\"level\":3,\"text\":\"Preview\",\"anchor\":\"preview\"},{\"id\":\"pagination\",\"level\":3,\"text\":\"Pagination\",\"anchor\":\"pagination\"},{\"id\":\"list-searchable-fields\",\"level\":3,\"text\":\"List Searchable Fields\",\"anchor\":\"list-searchable-fields\"}],\"keywords\":\"admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Collections\",\"order\":20,\"title\":\"Collection Admin Config\"},{\"slug\":\"globals\",\"content\":\"$70\",\"desc\":\"\",\"headings\":[{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"custom-components\",\"level\":3,\"text\":\"Custom Components\",\"anchor\":\"custom-components\"},{\"id\":\"preview\",\"level\":3,\"text\":\"Preview\",\"anchor\":\"preview\"}],\"keywords\":\"admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Globals\",\"order\":30,\"title\":\"Global Admin Config\"},{\"slug\":\"components\",\"content\":\"$71\",\"desc\":\"Fully customize your Admin Panel by swapping in your own React components. Add fields, remove views, update routes and change functions to sculpt your perfect Dashboard.\",\"headings\":[{\"id\":\"defining-custom-components\",\"level\":2,\"text\":\"Defining Custom Components\",\"anchor\":\"defining-custom-components\"},{\"id\":\"component-paths\",\"level\":3,\"text\":\"Component Paths\",\"anchor\":\"component-paths\"},{\"id\":\"config-options\",\"level\":3,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"import-map\",\"level\":3,\"text\":\"Import Map\",\"anchor\":\"import-map\"},{\"id\":\"building-custom-components\",\"level\":2,\"text\":\"Building Custom Components\",\"anchor\":\"building-custom-components\"},{\"id\":\"default-props\",\"level\":3,\"text\":\"Default Props\",\"anchor\":\"default-props\"},{\"id\":\"custom-props\",\"level\":3,\"text\":\"Custom Props\",\"anchor\":\"custom-props\"},{\"id\":\"client-components\",\"level\":3,\"text\":\"Client Components\",\"anchor\":\"client-components\"},{\"id\":\"accessing-the-payload-config\",\"level\":3,\"text\":\"Accessing the Payload Config\",\"anchor\":\"accessing-the-payload-config\"},{\"id\":\"getting-the-current-language\",\"level\":3,\"text\":\"Getting the Current Language\",\"anchor\":\"getting-the-current-language\"},{\"id\":\"getting-the-current-locale\",\"level\":3,\"text\":\"Getting the Current Locale\",\"anchor\":\"getting-the-current-locale\"},{\"id\":\"using-hooks\",\"level\":3,\"text\":\"Using Hooks\",\"anchor\":\"using-hooks\"},{\"id\":\"adding-styles\",\"level\":3,\"text\":\"Adding Styles\",\"anchor\":\"adding-styles\"},{\"id\":\"root-components\",\"level\":2,\"text\":\"Root Components\",\"anchor\":\"root-components\"},{\"id\":\"custom-providers\",\"level\":3,\"text\":\"Custom Providers\",\"anchor\":\"custom-providers\"}],\"keywords\":\"admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Custom Components\",\"order\":40,\"title\":\"Swap in your own React components\"},{\"slug\":\"views\",\"content\":\"$72\",\"desc\":\"\",\"headings\":[{\"id\":\"root-views\",\"level\":2,\"text\":\"Root Views\",\"anchor\":\"root-views\"},{\"id\":\"adding-new-views\",\"level\":3,\"text\":\"Adding New Views\",\"anchor\":\"adding-new-views\"},{\"id\":\"collection-views\",\"level\":2,\"text\":\"Collection Views\",\"anchor\":\"collection-views\"},{\"id\":\"global-views\",\"level\":2,\"text\":\"Global Views\",\"anchor\":\"global-views\"},{\"id\":\"document-views\",\"level\":2,\"text\":\"Document Views\",\"anchor\":\"document-views\"},{\"id\":\"document-tabs\",\"level\":3,\"text\":\"Document Tabs\",\"anchor\":\"document-tabs\"},{\"id\":\"building-custom-views\",\"level\":2,\"text\":\"Building Custom Views\",\"anchor\":\"building-custom-views\"},{\"id\":\"default-props\",\"level\":3,\"text\":\"Default Props\",\"anchor\":\"default-props\"}],\"keywords\":\"\",\"label\":\"Customizing Views\",\"order\":50,\"title\":\"Customizing Views\"},{\"slug\":\"fields\",\"content\":\"$73\",\"desc\":\"\",\"headings\":[{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"field-descriptions\",\"level\":2,\"text\":\"Field Descriptions\",\"anchor\":\"field-descriptions\"},{\"id\":\"conditional-logic\",\"level\":2,\"text\":\"Conditional Logic\",\"anchor\":\"conditional-logic\"},{\"id\":\"custom-components\",\"level\":2,\"text\":\"Custom Components\",\"anchor\":\"custom-components\"},{\"id\":\"field\",\"level\":3,\"text\":\"Field\",\"anchor\":\"field\"},{\"id\":\"cell\",\"level\":3,\"text\":\"Cell\",\"anchor\":\"cell\"},{\"id\":\"filter\",\"level\":3,\"text\":\"Filter\",\"anchor\":\"filter\"},{\"id\":\"label\",\"level\":3,\"text\":\"Label\",\"anchor\":\"label\"},{\"id\":\"description\",\"level\":3,\"text\":\"Description\",\"anchor\":\"description\"},{\"id\":\"error\",\"level\":3,\"text\":\"Error\",\"anchor\":\"error\"},{\"id\":\"afterinput-and-beforeinput\",\"level\":3,\"text\":\"afterInput and beforeInput\",\"anchor\":\"afterinput-and-beforeinput\"}],\"keywords\":\"\",\"label\":\"Customizing Fields\",\"order\":60,\"title\":\"Customizing Fields\"},{\"slug\":\"hooks\",\"content\":\"$74\",\"desc\":\"Make use of all of the powerful React hooks that Payload provides.\",\"headings\":[{\"id\":\"usefield\",\"level\":2,\"text\":\"useField\",\"anchor\":\"usefield\"},{\"id\":\"useformfields\",\"level\":2,\"text\":\"useFormFields\",\"anchor\":\"useformfields\"},{\"id\":\"useallformfields\",\"level\":2,\"text\":\"useAllFormFields\",\"anchor\":\"useallformfields\"},{\"id\":\"useform\",\"level\":2,\"text\":\"useForm\",\"anchor\":\"useform\"},{\"id\":\"usecollapsible\",\"level\":2,\"text\":\"useCollapsible\",\"anchor\":\"usecollapsible\"},{\"id\":\"usedocumentinfo\",\"level\":2,\"text\":\"useDocumentInfo\",\"anchor\":\"usedocumentinfo\"},{\"id\":\"uselocale\",\"level\":2,\"text\":\"useLocale\",\"anchor\":\"uselocale\"},{\"id\":\"useauth\",\"level\":2,\"text\":\"useAuth\",\"anchor\":\"useauth\"},{\"id\":\"useconfig\",\"level\":2,\"text\":\"useConfig\",\"anchor\":\"useconfig\"},{\"id\":\"useeditdepth\",\"level\":2,\"text\":\"useEditDepth\",\"anchor\":\"useeditdepth\"},{\"id\":\"usepreferences\",\"level\":2,\"text\":\"usePreferences\",\"anchor\":\"usepreferences\"},{\"id\":\"usetheme\",\"level\":2,\"text\":\"useTheme\",\"anchor\":\"usetheme\"},{\"id\":\"usetablecolumns\",\"level\":2,\"text\":\"useTableColumns\",\"anchor\":\"usetablecolumns\"},{\"id\":\"usedocumentevents\",\"level\":2,\"text\":\"useDocumentEvents\",\"anchor\":\"usedocumentevents\"}],\"keywords\":\"admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"React Hooks\",\"order\":70,\"title\":\"React Hooks\"},{\"slug\":\"metadata\",\"content\":\"$75\",\"desc\":\"Customize the metadata of your pages within the Admin Panel\",\"headings\":[{\"id\":\"root-metadata\",\"level\":2,\"text\":\"Root Metadata\",\"anchor\":\"root-metadata\"},{\"id\":\"icons\",\"level\":3,\"text\":\"Icons\",\"anchor\":\"icons\"},{\"id\":\"open-graph\",\"level\":3,\"text\":\"Open Graph\",\"anchor\":\"open-graph\"},{\"id\":\"collection-metadata\",\"level\":2,\"text\":\"Collection Metadata\",\"anchor\":\"collection-metadata\"},{\"id\":\"global-metadata\",\"level\":2,\"text\":\"Global Metadata\",\"anchor\":\"global-metadata\"},{\"id\":\"view-metadata\",\"level\":2,\"text\":\"View Metadata\",\"anchor\":\"view-metadata\"}],\"keywords\":\"admin, components, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Metadata\",\"order\":70,\"title\":\"Page Metadata\"},{\"slug\":\"preferences\",\"content\":\"$76\",\"desc\":\"Store the preferences of your users as they interact with the Admin Panel.\",\"headings\":[{\"id\":\"use-cases\",\"level\":2,\"text\":\"Use Cases\",\"anchor\":\"use-cases\"},{\"id\":\"database\",\"level\":2,\"text\":\"Database\",\"anchor\":\"database\"},{\"id\":\"apis\",\"level\":2,\"text\":\"APIs\",\"anchor\":\"apis\"},{\"id\":\"adding-or-reading-preferences-in-your-own-components\",\"level\":2,\"text\":\"Adding or reading Preferences in your own components\",\"anchor\":\"adding-or-reading-preferences-in-your-own-components\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"admin, preferences, custom, customize, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Preferences\",\"order\":70,\"title\":\"Managing User Preferences\"},{\"slug\":\"customizing-css\",\"content\":\"$77\",\"desc\":\"Customize the Payload Admin Panel further by adding your own CSS or SCSS style sheet to the configuration, powerful theme and design options are waiting for you.\",\"headings\":[{\"id\":\"global-css\",\"level\":2,\"text\":\"Global CSS\",\"anchor\":\"global-css\"},{\"id\":\"specificity-rules\",\"level\":3,\"text\":\"Specificity rules\",\"anchor\":\"specificity-rules\"},{\"id\":\"re-using-payload-scss-variables-and-utilities\",\"level\":2,\"text\":\"Re-using Payload SCSS variables and utilities\",\"anchor\":\"re-using-payload-scss-variables-and-utilities\"},{\"id\":\"css-library\",\"level\":2,\"text\":\"CSS Library\",\"anchor\":\"css-library\"}],\"keywords\":\"admin, css, scss, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Customizing CSS\",\"order\":80,\"title\":\"Customizing CSS \u0026 SCSS\"},{\"slug\":\"locked-documents\",\"content\":\"$78\",\"desc\":\"Ensure your documents are locked during editing to prevent concurrent changes from multiple users and maintain data integrity.\",\"headings\":[{\"id\":\"how-it-works\",\"level\":2,\"text\":\"How it works\",\"anchor\":\"how-it-works\"},{\"id\":\"config-options\",\"level\":3,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"impact-on-apis\",\"level\":3,\"text\":\"Impact on APIs\",\"anchor\":\"impact-on-apis\"}],\"keywords\":\"locking, document locking, edit locking, document, concurrency, Payload, headless, Content Management System, cms, javascript, react, node, nextjs\",\"label\":\"Document Locking\",\"order\":90,\"title\":\"Document Locking\"}]},{\"slug\":\"Authentication\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$79\",\"desc\":\"Payload provides highly secure user Authentication out of the box, and you can fully customize, override, or remove the default Authentication support.\",\"headings\":[{\"id\":\"config-options\",\"level\":2,\"text\":\"Config Options\",\"anchor\":\"config-options\"},{\"id\":\"login-with-username\",\"level\":3,\"text\":\"Login With Username\",\"anchor\":\"login-with-username\"},{\"id\":\"auto-login\",\"level\":2,\"text\":\"Auto-Login\",\"anchor\":\"auto-login\"},{\"id\":\"operations\",\"level\":2,\"text\":\"Operations\",\"anchor\":\"operations\"},{\"id\":\"strategies\",\"level\":2,\"text\":\"Strategies\",\"anchor\":\"strategies\"},{\"id\":\"http-only-cookies\",\"level\":3,\"text\":\"HTTP-Only Cookies\",\"anchor\":\"http-only-cookies\"},{\"id\":\"json-web-tokens\",\"level\":3,\"text\":\"JSON Web Tokens\",\"anchor\":\"json-web-tokens\"},{\"id\":\"api-keys\",\"level\":3,\"text\":\"API Keys\",\"anchor\":\"api-keys\"},{\"id\":\"custom-strategies\",\"level\":3,\"text\":\"Custom Strategies\",\"anchor\":\"custom-strategies\"}],\"keywords\":\"authentication, config, configuration, overview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Authentication Overview\"},{\"slug\":\"operations\",\"content\":\"$7a\",\"desc\":\"Enabling Authentication automatically makes key operations available such as Login, Logout, Verify, Unlock, Reset Password and more.\",\"headings\":[{\"id\":\"access\",\"level\":2,\"text\":\"Access\",\"anchor\":\"access\"},{\"id\":\"me\",\"level\":2,\"text\":\"Me\",\"anchor\":\"me\"},{\"id\":\"login\",\"level\":2,\"text\":\"Login\",\"anchor\":\"login\"},{\"id\":\"logout\",\"level\":2,\"text\":\"Logout\",\"anchor\":\"logout\"},{\"id\":\"refresh\",\"level\":2,\"text\":\"Refresh\",\"anchor\":\"refresh\"},{\"id\":\"verify-by-email\",\"level\":2,\"text\":\"Verify by Email\",\"anchor\":\"verify-by-email\"},{\"id\":\"unlock\",\"level\":2,\"text\":\"Unlock\",\"anchor\":\"unlock\"},{\"id\":\"forgot-password\",\"level\":2,\"text\":\"Forgot Password\",\"anchor\":\"forgot-password\"},{\"id\":\"reset-password\",\"level\":2,\"text\":\"Reset Password\",\"anchor\":\"reset-password\"}],\"keywords\":\"authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Operations\",\"order\":20,\"title\":\"Authentication Operations\"},{\"slug\":\"email\",\"content\":\"$7b\",\"desc\":\"Email Verification allows users to verify their email address before they're account is fully activated. Email Verification ties directly into the Email functionality that Payload provides.\",\"headings\":[{\"id\":\"email-verification\",\"level\":2,\"text\":\"Email Verification\",\"anchor\":\"email-verification\"},{\"id\":\"forgot-password\",\"level\":2,\"text\":\"Forgot Password\",\"anchor\":\"forgot-password\"}],\"keywords\":\"authentication, email, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Email Verification\",\"order\":30,\"title\":\"Authentication Emails\"},{\"slug\":\"cookies\",\"content\":\"$7c\",\"desc\":\"Enable HTTP Cookie based authentication to interface with Payload.\",\"headings\":[{\"id\":\"automatic-browser-inclusion\",\"level\":3,\"text\":\"Automatic browser inclusion\",\"anchor\":\"automatic-browser-inclusion\"},{\"id\":\"http-authentication\",\"level\":3,\"text\":\"HTTP Authentication\",\"anchor\":\"http-authentication\"},{\"id\":\"csrf-attacks\",\"level\":3,\"text\":\"CSRF Attacks\",\"anchor\":\"csrf-attacks\"},{\"id\":\"csrf-prevention\",\"level\":3,\"text\":\"CSRF Prevention\",\"anchor\":\"csrf-prevention\"}],\"keywords\":\"authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Cookie Strategy\",\"order\":40,\"title\":\"Cookie Strategy\"},{\"slug\":\"jwt\",\"content\":\"$7d\",\"desc\":\"Enable JSON Web Token based authentication to interface with Payload.\",\"headings\":[{\"id\":\"identifying-users-via-the-authorization-header\",\"level\":3,\"text\":\"Identifying Users Via The Authorization Header\",\"anchor\":\"identifying-users-via-the-authorization-header\"},{\"id\":\"omitting-the-token\",\"level\":3,\"text\":\"Omitting The Token\",\"anchor\":\"omitting-the-token\"}],\"keywords\":\"authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"JWT Strategy\",\"order\":40,\"title\":\"JWT Strategy\"},{\"slug\":\"api-keys\",\"content\":\"$7e\",\"desc\":\"Enable API key based authentication to interface with Payload.\",\"headings\":[{\"id\":\"http-authentication\",\"level\":3,\"text\":\"HTTP Authentication\",\"anchor\":\"http-authentication\"},{\"id\":\"api-key-only-auth\",\"level\":3,\"text\":\"API Key Only Auth\",\"anchor\":\"api-key-only-auth\"}],\"keywords\":\"authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"API Key Strategy\",\"order\":50,\"title\":\"API Key Strategy\"},{\"slug\":\"custom-strategies\",\"content\":\"$7f\",\"desc\":\"Create custom authentication strategies to handle everything auth in Payload.\",\"headings\":[{\"id\":\"creating-a-strategy\",\"level\":3,\"text\":\"Creating a strategy\",\"anchor\":\"creating-a-strategy\"},{\"id\":\"example-strategy\",\"level\":3,\"text\":\"Example Strategy\",\"anchor\":\"example-strategy\"}],\"keywords\":\"authentication, config, configuration, overview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Custom Strategies\",\"order\":60,\"title\":\"Custom Strategies\"},{\"slug\":\"token-data\",\"content\":\"$80\",\"desc\":\"Storing data for read on the request object.\",\"headings\":[{\"id\":\"definining-token-data\",\"level\":3,\"text\":\"Definining Token Data\",\"anchor\":\"definining-token-data\"},{\"id\":\"using-token-data\",\"level\":3,\"text\":\"Using Token Data\",\"anchor\":\"using-token-data\"}],\"keywords\":\"authentication, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Token Data\",\"order\":70,\"title\":\"Token Data\"}]},{\"slug\":\"Rich-Text\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$81\",\"desc\":\"Rich Text within Payload is extremely powerful. We've combined the beauty of the Medium editor with the power of the Notion editor all in one place.\",\"headings\":[],\"keywords\":\"slatejs, lexical, rich text, json, custom editor, javascript, typescript\",\"label\":\"Overview\",\"order\":10,\"title\":\"Overview\"},{\"slug\":\"slate\",\"content\":\"$82\",\"desc\":\"The Slate editor has been supported by Payload since beta. It's very powerful and stores content as JSON, which unlocks a ton of power.\",\"headings\":[{\"id\":\"admin-options\",\"level\":2,\"text\":\"Admin Options\",\"anchor\":\"admin-options\"},{\"id\":\"relationship-element\",\"level\":3,\"text\":\"Relationship element\",\"anchor\":\"relationship-element\"},{\"id\":\"upload-element\",\"level\":3,\"text\":\"Upload element\",\"anchor\":\"upload-element\"},{\"id\":\"textalign-element\",\"level\":3,\"text\":\"TextAlign element\",\"anchor\":\"textalign-element\"},{\"id\":\"specifying-which-elements-and-leaves-to-allow\",\"level\":3,\"text\":\"Specifying which elements and leaves to allow\",\"anchor\":\"specifying-which-elements-and-leaves-to-allow\"},{\"id\":\"building-custom-elements-and-leaves\",\"level\":3,\"text\":\"Building custom elements and leaves\",\"anchor\":\"building-custom-elements-and-leaves\"},{\"id\":\"example\",\"level\":3,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"generating-html\",\"level\":3,\"text\":\"Generating HTML\",\"anchor\":\"generating-html\"},{\"id\":\"built-in-slatejs-plugins\",\"level\":3,\"text\":\"Built-in SlateJS Plugins\",\"anchor\":\"built-in-slatejs-plugins\"},{\"id\":\"typescript\",\"level\":3,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"slatejs, slate, rich text, editor, headless cms\",\"label\":\"Slate\",\"order\":20,\"title\":\"Slate Rich Text\"},{\"slug\":\"lexical\",\"content\":\"\\nThe new lexical docs can be found at [Lexical](../lexical/overview).\\n\",\"desc\":\"Built by Meta, Lexical is an incredibly powerful rich text editor, and it works beautifully within Payload.\",\"headings\":[],\"keywords\":\"lexical, rich text, editor, headless cms\",\"label\":\"Lexical\",\"order\":30,\"title\":\"Lexical Rich Text\"}]},{\"slug\":\"Lexical\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$83\",\"desc\":\"Built by Meta, Lexical is an incredibly powerful rich text editor, and it works beautifully within Payload.\",\"headings\":[{\"id\":\"extending-the-lexical-editor-with-features\",\"level\":2,\"text\":\"Extending the lexical editor with Features\",\"anchor\":\"extending-the-lexical-editor-with-features\"},{\"id\":\"features-the-building-blocks\",\"level\":3,\"text\":\"Features: The Building Blocks\",\"anchor\":\"features-the-building-blocks\"},{\"id\":\"integrating-new-features\",\"level\":3,\"text\":\"Integrating New Features\",\"anchor\":\"integrating-new-features\"},{\"id\":\"features-overview\",\"level\":2,\"text\":\"Features overview\",\"anchor\":\"features-overview\"},{\"id\":\"creating-your-own-custom-feature\",\"level\":2,\"text\":\"Creating your own, custom Feature\",\"anchor\":\"creating-your-own-custom-feature\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"},{\"id\":\"automatic-type-generation\",\"level\":3,\"text\":\"Automatic type generation\",\"anchor\":\"automatic-type-generation\"}],\"keywords\":\"lexical, rich text, editor, headless cms\",\"label\":\"Overview\",\"order\":10,\"title\":\"Lexical Overview\"},{\"slug\":\"converters\",\"content\":\"$84\",\"desc\":\"Conversion between lexical, markdown and html\",\"headings\":[{\"id\":\"lexical-jsx\",\"level\":2,\"text\":\"Lexical =\u003e JSX\",\"anchor\":\"lexical-jsx\"},{\"id\":\"converting-lexical-blocks-to-jsx\",\"level\":3,\"text\":\"Converting Lexical Blocks to JSX\",\"anchor\":\"converting-lexical-blocks-to-jsx\"},{\"id\":\"lexical-html\",\"level\":2,\"text\":\"Lexical =\u003e HTML\",\"anchor\":\"lexical-html\"},{\"id\":\"outputting-html-from-the-collection\",\"level\":3,\"text\":\"Outputting HTML from the Collection\",\"anchor\":\"outputting-html-from-the-collection\"},{\"id\":\"generating-html-anywhere-on-the-server\",\"level\":3,\"text\":\"Generating HTML anywhere on the server\",\"anchor\":\"generating-html-anywhere-on-the-server\"},{\"id\":\"css\",\"level\":3,\"text\":\"CSS\",\"anchor\":\"css\"},{\"id\":\"creating-your-own-html-converter\",\"level\":3,\"text\":\"Creating your own HTML Converter\",\"anchor\":\"creating-your-own-html-converter\"},{\"id\":\"embedding-the-html-converter-in-your-feature\",\"level\":3,\"text\":\"Embedding the HTML Converter in your Feature\",\"anchor\":\"embedding-the-html-converter-in-your-feature\"},{\"id\":\"headless-editor\",\"level\":2,\"text\":\"Headless Editor\",\"anchor\":\"headless-editor\"},{\"id\":\"getting-the-editor-config\",\"level\":3,\"text\":\"Getting the editor config\",\"anchor\":\"getting-the-editor-config\"},{\"id\":\"getting-the-editor-config-from-an-existing-field\",\"level\":3,\"text\":\"Getting the editor config from an existing field\",\"anchor\":\"getting-the-editor-config-from-an-existing-field\"},{\"id\":\"html-lexical\",\"level\":2,\"text\":\"HTML =\u003e Lexical\",\"anchor\":\"html-lexical\"},{\"id\":\"markdown-lexical\",\"level\":2,\"text\":\"Markdown =\u003e Lexical\",\"anchor\":\"markdown-lexical\"},{\"id\":\"lexical-markdown\",\"level\":2,\"text\":\"Lexical =\u003e Markdown\",\"anchor\":\"lexical-markdown\"},{\"id\":\"lexical-plain-text\",\"level\":2,\"text\":\"Lexical =\u003e Plain Text\",\"anchor\":\"lexical-plain-text\"}],\"keywords\":\"lexical, rich text, editor, headless cms, convert, html, mdx, markdown, md, conversion, export\",\"label\":\"Converters\",\"order\":20,\"title\":\"Lexical Converters\"},{\"slug\":\"migration\",\"content\":\"$85\",\"desc\":\"Migration from slate and payload-plugin-lexical to lexical\",\"headings\":[{\"id\":\"migrating-from-slate\",\"level\":2,\"text\":\"Migrating from Slate\",\"anchor\":\"migrating-from-slate\"},{\"id\":\"migration-via-migration-script-recommended\",\"level\":3,\"text\":\"Migration via Migration Script (Recommended)\",\"anchor\":\"migration-via-migration-script-recommended\"},{\"id\":\"migration-via-slatetolexicalfeature\",\"level\":3,\"text\":\"Migration via SlateToLexicalFeature\",\"anchor\":\"migration-via-slatetolexicalfeature\"},{\"id\":\"converting-custom-slate-nodes\",\"level\":3,\"text\":\"Converting custom Slate nodes\",\"anchor\":\"converting-custom-slate-nodes\"},{\"id\":\"migrating-from-payload-plugin-lexical\",\"level\":2,\"text\":\"Migrating from payload-plugin-lexical\",\"anchor\":\"migrating-from-payload-plugin-lexical\"},{\"id\":\"migrating-lexical-data-from-old-version-to-new-version\",\"level\":2,\"text\":\"Migrating lexical data from old version to new version\",\"anchor\":\"migrating-lexical-data-from-old-version-to-new-version\"}],\"keywords\":\"lexical, rich text, editor, headless cms, migrate, migration\",\"label\":\"Migration\",\"order\":30,\"title\":\"Lexical Migration\"},{\"slug\":\"building-custom-features\",\"content\":\"$86\",\"desc\":\"Building custom lexical features\",\"headings\":[{\"id\":\"server-feature\",\"level\":2,\"text\":\"Server Feature\",\"anchor\":\"server-feature\"},{\"id\":\"i18n\",\"level\":3,\"text\":\"i18n\",\"anchor\":\"i18n\"},{\"id\":\"server-feature-markdown-transformers\",\"level\":3,\"text\":\"Markdown Transformers\",\"anchor\":\"server-feature-markdown-transformers\"},{\"id\":\"server-feature-nodes\",\"level\":3,\"text\":\"Nodes\",\"anchor\":\"server-feature-nodes\"},{\"id\":\"feature-load-order\",\"level\":3,\"text\":\"Feature load order\",\"anchor\":\"feature-load-order\"},{\"id\":\"client-feature\",\"level\":2,\"text\":\"Client Feature\",\"anchor\":\"client-feature\"},{\"id\":\"client-feature-nodes\",\"level\":3,\"text\":\"Nodes\",\"anchor\":\"client-feature-nodes\"},{\"id\":\"plugins\",\"level\":3,\"text\":\"Plugins\",\"anchor\":\"plugins\"},{\"id\":\"toolbar-groups\",\"level\":3,\"text\":\"Toolbar groups\",\"anchor\":\"toolbar-groups\"},{\"id\":\"toolbar-items\",\"level\":3,\"text\":\"Toolbar items\",\"anchor\":\"toolbar-items\"},{\"id\":\"slash-menu-groups\",\"level\":3,\"text\":\"Slash Menu groups\",\"anchor\":\"slash-menu-groups\"},{\"id\":\"slash-menu-items\",\"level\":3,\"text\":\"Slash Menu items\",\"anchor\":\"slash-menu-items\"},{\"id\":\"client-feature-markdown-transformers\",\"level\":3,\"text\":\"Markdown Transformers\",\"anchor\":\"client-feature-markdown-transformers\"},{\"id\":\"providers\",\"level\":3,\"text\":\"Providers\",\"anchor\":\"providers\"},{\"id\":\"props\",\"level\":2,\"text\":\"Props\",\"anchor\":\"props\"},{\"id\":\"bringing-props-from-the-server-to-the-client\",\"level\":3,\"text\":\"Bringing props from the server to the client\",\"anchor\":\"bringing-props-from-the-server-to-the-client\"},{\"id\":\"more-information\",\"level\":2,\"text\":\"More information\",\"anchor\":\"more-information\"}],\"keywords\":\"lexical, rich text, editor, headless cms, feature, features\",\"label\":\"Custom Features\",\"order\":40,\"title\":\"Lexical Building Custom Features\"}]},{\"slug\":\"Live-Preview\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$87\",\"desc\":\"With Live Preview you can render your front-end application directly within the Admin Panel. Your changes take effect as you type. No save needed.\",\"headings\":[{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"url\",\"level\":3,\"text\":\"URL\",\"anchor\":\"url\"},{\"id\":\"breakpoints\",\"level\":3,\"text\":\"Breakpoints\",\"anchor\":\"breakpoints\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"live preview, preview, live, iframe, iframe preview, visual editing, design\",\"label\":\"Overview\",\"order\":10,\"title\":\"Live Preview\"},{\"slug\":\"frontend\",\"content\":\"\\nThere are two ways to use Live Preview in your own application depending on whether your front-end framework supports Server Components:\\n\\n- [Server-side Live Preview (suggested)](./server)\\n- [Client-side Live Preview](./client)\\n\\n\u003cBanner type=\\\"info\\\"\u003e\\n We suggest using server-side Live Preview if your framework supports Server Components, it is both simpler to setup and more performant to run than the client-side alternative.\\n\u003c/Banner\u003e\\n\",\"desc\":\"Learn how to implement Live Preview in your front-end application.\",\"headings\":[],\"keywords\":\"live preview, frontend, react, next.js, vue, nuxt.js, svelte, hook, useLivePreview\",\"label\":\"Frontend\",\"order\":20,\"title\":\"Implementing Live Preview in your frontend\"},{\"slug\":\"server\",\"content\":\"$88\",\"desc\":\"Learn how to implement Live Preview in your server-side front-end application.\",\"headings\":[{\"id\":\"react\",\"level\":2,\"text\":\"React\",\"anchor\":\"react\"},{\"id\":\"building-your-own-router-refresh-component\",\"level\":2,\"text\":\"Building your own router refresh component\",\"anchor\":\"building-your-own-router-refresh-component\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"troubleshooting\",\"level\":2,\"text\":\"Troubleshooting\",\"anchor\":\"troubleshooting\"}],\"keywords\":\"live preview, frontend, react, next.js, vue, nuxt.js, svelte, hook, useLivePreview\",\"label\":\"Server-side\",\"order\":30,\"title\":\"Server-side Live Preview\"},{\"slug\":\"client\",\"content\":\"$89\",\"desc\":\"Learn how to implement Live Preview in your client-side front-end application.\",\"headings\":[{\"id\":\"frameworks\",\"level\":2,\"text\":\"Frameworks\",\"anchor\":\"frameworks\"},{\"id\":\"react\",\"level\":3,\"text\":\"React\",\"anchor\":\"react\"},{\"id\":\"vue\",\"level\":3,\"text\":\"Vue\",\"anchor\":\"vue\"},{\"id\":\"building-your-own-hook\",\"level\":2,\"text\":\"Building your own hook\",\"anchor\":\"building-your-own-hook\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"troubleshooting\",\"level\":2,\"text\":\"Troubleshooting\",\"anchor\":\"troubleshooting\"}],\"keywords\":\"live preview, frontend, react, next.js, vue, nuxt.js, svelte, hook, useLivePreview\",\"label\":\"Client-side\",\"order\":40,\"title\":\"Client-side Live Preview\"}]},{\"slug\":\"Versions\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$8a\",\"desc\":\"Keep a version history or audit log of changes and publish collection documents and globals.\",\"headings\":[{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"versions-enabled-drafts-disabled\",\"level\":3,\"text\":\"Versions enabled, drafts disabled\",\"anchor\":\"versions-enabled-drafts-disabled\"},{\"id\":\"versions-and-drafts-enabled\",\"level\":3,\"text\":\"Versions and drafts enabled\",\"anchor\":\"versions-and-drafts-enabled\"},{\"id\":\"versions-drafts-and-autosave-enabled\",\"level\":3,\"text\":\"Versions, drafts, and autosave enabled\",\"anchor\":\"versions-drafts-and-autosave-enabled\"},{\"id\":\"collection-config\",\"level\":2,\"text\":\"Collection config\",\"anchor\":\"collection-config\"},{\"id\":\"global-config\",\"level\":2,\"text\":\"Global config\",\"anchor\":\"global-config\"},{\"id\":\"database-impact\",\"level\":3,\"text\":\"Database impact\",\"anchor\":\"database-impact\"},{\"id\":\"version-operations\",\"level\":2,\"text\":\"Version operations\",\"anchor\":\"version-operations\"},{\"id\":\"find\",\"level\":3,\"text\":\"Find\",\"anchor\":\"find\"},{\"id\":\"find-by-id\",\"level\":3,\"text\":\"Find by ID\",\"anchor\":\"find-by-id\"},{\"id\":\"restore\",\"level\":3,\"text\":\"Restore\",\"anchor\":\"restore\"},{\"id\":\"find\",\"level\":3,\"text\":\"Find\",\"anchor\":\"find\"},{\"id\":\"find-by-id\",\"level\":3,\"text\":\"Find by ID\",\"anchor\":\"find-by-id\"},{\"id\":\"restore\",\"level\":3,\"text\":\"Restore\",\"anchor\":\"restore\"},{\"id\":\"access-control\",\"level\":2,\"text\":\"Access Control\",\"anchor\":\"access-control\"}],\"keywords\":\"version history, revisions, audit log, draft, publish, restore, autosave, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Versions\"},{\"slug\":\"drafts\",\"content\":\"$8b\",\"desc\":\"Enable drafts on collection documents or globals and build true preview environments for your data.\",\"headings\":[{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"database-changes\",\"level\":2,\"text\":\"Database changes\",\"anchor\":\"database-changes\"},{\"id\":\"draft-api\",\"level\":2,\"text\":\"Draft API\",\"anchor\":\"draft-api\"},{\"id\":\"controlling-who-can-see-collection-drafts\",\"level\":2,\"text\":\"Controlling who can see Collection drafts\",\"anchor\":\"controlling-who-can-see-collection-drafts\"},{\"id\":\"unpublishing-drafts\",\"level\":2,\"text\":\"Unpublishing drafts\",\"anchor\":\"unpublishing-drafts\"},{\"id\":\"reverting-to-published\",\"level\":2,\"text\":\"Reverting to published\",\"anchor\":\"reverting-to-published\"}],\"keywords\":\"version history, drafts, preview, draft, restore, publish, autosave, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Drafts\",\"order\":20,\"title\":\"Drafts\"},{\"slug\":\"autosave\",\"content\":\"$8c\",\"desc\":\"Using Payload's Draft functionality, you can configure your collections and globals to autosave changes as drafts, and publish only you're ready.\",\"headings\":[{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"autosave-api\",\"level\":2,\"text\":\"Autosave API\",\"anchor\":\"autosave-api\"},{\"id\":\"how-autosaves-are-stored\",\"level\":3,\"text\":\"How autosaves are stored\",\"anchor\":\"how-autosaves-are-stored\"}],\"keywords\":\"version history, revisions, audit log, draft, publish, autosave, Content Management System, cms, headless, javascript, node, react, nextjss\",\"label\":\"Autosave\",\"order\":30,\"title\":\"Autosave\"}]},{\"slug\":\"Upload\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$8d\",\"desc\":\"Payload supports uploads, storage and management of files directly on your server, combined with powerful file access control.\",\"headings\":[{\"id\":\"enabling-uploads\",\"level\":2,\"text\":\"Enabling Uploads\",\"anchor\":\"enabling-uploads\"},{\"id\":\"collection-upload-options\",\"level\":3,\"text\":\"Collection Upload Options\",\"anchor\":\"collection-upload-options\"},{\"id\":\"payload-wide-upload-options\",\"level\":3,\"text\":\"Payload-wide Upload Options\",\"anchor\":\"payload-wide-upload-options\"},{\"id\":\"custom-filename-via-hooks\",\"level\":3,\"text\":\"Custom filename via hooks\",\"anchor\":\"custom-filename-via-hooks\"},{\"id\":\"image-sizes\",\"level\":2,\"text\":\"Image Sizes\",\"anchor\":\"image-sizes\"},{\"id\":\"crop-and-focal-point-selector\",\"level\":2,\"text\":\"Crop and Focal Point Selector\",\"anchor\":\"crop-and-focal-point-selector\"},{\"id\":\"disabling-local-upload-storage\",\"level\":2,\"text\":\"Disabling Local Upload Storage\",\"anchor\":\"disabling-local-upload-storage\"},{\"id\":\"admin-thumbnails\",\"level\":2,\"text\":\"Admin Thumbnails\",\"anchor\":\"admin-thumbnails\"},{\"id\":\"mimetypes\",\"level\":2,\"text\":\"MimeTypes\",\"anchor\":\"mimetypes\"},{\"id\":\"uploading-files\",\"level\":2,\"text\":\"Uploading Files\",\"anchor\":\"uploading-files\"},{\"id\":\"access-control\",\"level\":2,\"text\":\"Access Control\",\"anchor\":\"access-control\"}],\"keywords\":\"uploads, images, media, overview, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Uploads\"},{\"slug\":\"storage-adapters\",\"content\":\"$8e\",\"desc\":\"Payload provides additional storage adapters to handle file uploads. These adapters allow you to store files in different locations, such as Amazon S3, Vercel Blob Storage, Google Cloud Storage, Uploadthing, and more.\",\"headings\":[{\"id\":\"vercel-blob-storage\",\"level\":2,\"text\":\"Vercel Blob Storage\",\"anchor\":\"vercel-blob-storage\"},{\"id\":\"vercel-blob-installation\",\"level\":3,\"text\":\"Installation\",\"anchor\":\"vercel-blob-installation\"},{\"id\":\"vercel-blob-usage\",\"level\":3,\"text\":\"Usage\",\"anchor\":\"vercel-blob-usage\"},{\"id\":\"vercel-blob-configuration\",\"level\":3,\"text\":\"Configuration Options\",\"anchor\":\"vercel-blob-configuration\"},{\"id\":\"s3-storage\",\"level\":2,\"text\":\"S3 Storage\",\"anchor\":\"s3-storage\"},{\"id\":\"s3-installation\",\"level\":3,\"text\":\"Installation\",\"anchor\":\"s3-installation\"},{\"id\":\"s3-usage\",\"level\":3,\"text\":\"Usage\",\"anchor\":\"s3-usage\"},{\"id\":\"s3-configuration\",\"level\":3,\"text\":\"Configuration Options\",\"anchor\":\"s3-configuration\"},{\"id\":\"azure-blob-storage\",\"level\":2,\"text\":\"Azure Blob Storage\",\"anchor\":\"azure-blob-storage\"},{\"id\":\"azure-installation\",\"level\":3,\"text\":\"Installation\",\"anchor\":\"azure-installation\"},{\"id\":\"azure-usage\",\"level\":3,\"text\":\"Usage\",\"anchor\":\"azure-usage\"},{\"id\":\"azure-configuration\",\"level\":3,\"text\":\"Configuration Options\",\"anchor\":\"azure-configuration\"},{\"id\":\"google-cloud-storage\",\"level\":2,\"text\":\"Google Cloud Storage\",\"anchor\":\"google-cloud-storage\"},{\"id\":\"gcs-installation\",\"level\":3,\"text\":\"Installation\",\"anchor\":\"gcs-installation\"},{\"id\":\"gcs-usage\",\"level\":3,\"text\":\"Usage\",\"anchor\":\"gcs-usage\"},{\"id\":\"gcs-configuration\",\"level\":3,\"text\":\"Configuration Options\",\"anchor\":\"gcs-configuration\"},{\"id\":\"uploadthing-storage\",\"level\":2,\"text\":\"Uploadthing Storage\",\"anchor\":\"uploadthing-storage\"},{\"id\":\"uploadthing-installation\",\"level\":3,\"text\":\"Installation\",\"anchor\":\"uploadthing-installation\"},{\"id\":\"uploadthing-usage\",\"level\":3,\"text\":\"Usage\",\"anchor\":\"uploadthing-usage\"},{\"id\":\"uploadthing-configuration\",\"level\":3,\"text\":\"Configuration Options\",\"anchor\":\"uploadthing-configuration\"},{\"id\":\"custom-storage-adapters\",\"level\":2,\"text\":\"Custom Storage Adapters\",\"anchor\":\"custom-storage-adapters\"},{\"id\":\"custom-installation\",\"level\":3,\"text\":\"Installation\",\"anchor\":\"custom-installation\"},{\"id\":\"custom-usage\",\"level\":3,\"text\":\"Usage\",\"anchor\":\"custom-usage\"},{\"id\":\"plugin-options\",\"level\":2,\"text\":\"Plugin options\",\"anchor\":\"plugin-options\"},{\"id\":\"collection-specific-options\",\"level\":2,\"text\":\"Collection-specific options\",\"anchor\":\"collection-specific-options\"},{\"id\":\"payload-access-control\",\"level\":2,\"text\":\"Payload Access Control\",\"anchor\":\"payload-access-control\"},{\"id\":\"conditionally-enabling-disabling\",\"level\":2,\"text\":\"Conditionally Enabling/Disabling\",\"anchor\":\"conditionally-enabling-disabling\"}],\"keywords\":\"uploads, images, media, storage, adapters, s3, vercel, google cloud, azure\",\"label\":\"Storage Adapters\",\"order\":20,\"title\":\"Storage Adapters\"}]},{\"slug\":\"Email\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$8f\",\"desc\":\"Payload uses an adapter pattern to enable email functionality. Set up email functions such as password resets, order confirmations and more.\",\"headings\":[{\"id\":\"introduction\",\"level\":2,\"text\":\"Introduction\",\"anchor\":\"introduction\"},{\"id\":\"configuration\",\"level\":2,\"text\":\"Configuration\",\"anchor\":\"configuration\"},{\"id\":\"default-configuration\",\"level\":3,\"text\":\"Default Configuration\",\"anchor\":\"default-configuration\"},{\"id\":\"email-adapter\",\"level\":3,\"text\":\"Email Adapter\",\"anchor\":\"email-adapter\"},{\"id\":\"official-email-adapters\",\"level\":3,\"text\":\"Official Email Adapters\",\"anchor\":\"official-email-adapters\"},{\"id\":\"nodemailer-configuration\",\"level\":2,\"text\":\"Nodemailer Configuration\",\"anchor\":\"nodemailer-configuration\"},{\"id\":\"use-smtp\",\"level\":2,\"text\":\"Use SMTP\",\"anchor\":\"use-smtp\"},{\"id\":\"resend-configuration\",\"level\":2,\"text\":\"Resend Configuration\",\"anchor\":\"resend-configuration\"},{\"id\":\"sending-mail\",\"level\":2,\"text\":\"Sending Mail\",\"anchor\":\"sending-mail\"},{\"id\":\"using-multiple-mail-providers\",\"level\":2,\"text\":\"Using multiple mail providers\",\"anchor\":\"using-multiple-mail-providers\"}],\"keywords\":\"email, overview, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Email Functionality\"}]},{\"slug\":\"Jobs-Queue\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$90\",\"desc\":\"Payload provides all you need to run job queues, which are helpful to offload long-running processes into separate workers.\",\"headings\":[{\"id\":\"example-use-cases\",\"level\":3,\"text\":\"Example use cases\",\"anchor\":\"example-use-cases\"},{\"id\":\"how-it-works\",\"level\":3,\"text\":\"How it works\",\"anchor\":\"how-it-works\"}],\"keywords\":\"jobs queue, application framework, typescript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Jobs Queue\"},{\"slug\":\"tasks\",\"content\":\"$91\",\"desc\":\"A Task is a distinct function declaration that can be run within Payload's Jobs Queue.\",\"headings\":[{\"id\":\"defining-tasks-in-the-config\",\"level\":3,\"text\":\"Defining tasks in the config\",\"anchor\":\"defining-tasks-in-the-config\"}],\"keywords\":\"jobs queue, application framework, typescript, node, react, nextjs\",\"label\":\"Tasks\",\"order\":20,\"title\":\"Tasks\"},{\"slug\":\"workflows\",\"content\":\"$92\",\"desc\":\"A Task is a distinct function declaration that can be run within Payload's Jobs Queue.\",\"headings\":[],\"keywords\":\"jobs queue, application framework, typescript, node, react, nextjs\",\"label\":\"Workflows\",\"order\":30,\"title\":\"Workflows\"},{\"slug\":\"jobs\",\"content\":\"$93\",\"desc\":\"A Job is a set of work that is offloaded from your APIs and will be processed at a later date.\",\"headings\":[],\"keywords\":\"jobs queue, application framework, typescript, node, react, nextjs\",\"label\":\"Jobs\",\"order\":40,\"title\":\"Jobs\"},{\"slug\":\"queues\",\"content\":\"$94\",\"desc\":\"A Queue is a specific group of jobs which can be executed in the order that they were added.\",\"headings\":[{\"id\":\"executing-jobs\",\"level\":2,\"text\":\"Executing jobs\",\"anchor\":\"executing-jobs\"}],\"keywords\":\"jobs queue, application framework, typescript, node, react, nextjs\",\"label\":\"Queues\",\"order\":50,\"title\":\"Queues\"}]},{\"slug\":\"TypeScript\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$95\",\"desc\":\"Payload is the most powerful TypeScript headless CMS available.\",\"headings\":[{\"id\":\"setting-up-from-scratch\",\"level\":2,\"text\":\"Setting up from Scratch\",\"anchor\":\"setting-up-from-scratch\"},{\"id\":\"using-payloads-exported-types\",\"level\":2,\"text\":\"Using Payload's Exported Types\",\"anchor\":\"using-payloads-exported-types\"},{\"id\":\"config-types\",\"level\":2,\"text\":\"Config Types\",\"anchor\":\"config-types\"},{\"id\":\"hook-types\",\"level\":2,\"text\":\"Hook Types\",\"anchor\":\"hook-types\"}],\"keywords\":\"headless cms, typescript, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"TypeScript - Overview\"},{\"slug\":\"generating-types\",\"content\":\"$96\",\"desc\":\"Generate your own TypeScript interfaces based on your collections and globals.\",\"headings\":[{\"id\":\"types-generation-script\",\"level\":2,\"text\":\"Types generation script\",\"anchor\":\"types-generation-script\"},{\"id\":\"disable-declare-statement\",\"level\":2,\"text\":\"Disable declare statement\",\"anchor\":\"disable-declare-statement\"},{\"id\":\"custom-output-file-path\",\"level\":2,\"text\":\"Custom output file path\",\"anchor\":\"custom-output-file-path\"},{\"id\":\"custom-generated-types\",\"level\":2,\"text\":\"Custom generated types\",\"anchor\":\"custom-generated-types\"},{\"id\":\"example-usage\",\"level\":2,\"text\":\"Example Usage\",\"anchor\":\"example-usage\"},{\"id\":\"custom-field-interfaces\",\"level\":2,\"text\":\"Custom Field Interfaces\",\"anchor\":\"custom-field-interfaces\"},{\"id\":\"using-your-types\",\"level\":2,\"text\":\"Using your types\",\"anchor\":\"using-your-types\"},{\"id\":\"adding-an-npm-script\",\"level\":3,\"text\":\"Adding an NPM script\",\"anchor\":\"adding-an-npm-script\"}],\"keywords\":\"headless cms, typescript, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Generating Types\",\"order\":20,\"title\":\"Generating TypeScript Interfaces\"}]}]},{\"groupLabel\":\"Ecosystem\",\"topics\":[{\"slug\":\"Plugins\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$97\",\"desc\":\"Plugins provide a great way to modularize Payload functionalities into easy-to-use enhancements and extensions of your Payload apps.\",\"headings\":[{\"id\":\"official-plugins\",\"level\":2,\"text\":\"Official Plugins\",\"anchor\":\"official-plugins\"},{\"id\":\"community-plugins\",\"level\":2,\"text\":\"Community Plugins\",\"anchor\":\"community-plugins\"},{\"id\":\"example\",\"level\":2,\"text\":\"Example\",\"anchor\":\"example\"}],\"keywords\":\"plugins, config, configuration, extensions, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Overview\",\"order\":10,\"title\":\"Plugins\"},{\"slug\":\"build-your-own\",\"content\":\"$98\",\"desc\":\"Starting to build your own plugin? Find everything you need and learn best practices with the Payload plugin template.\",\"headings\":[{\"id\":\"plugins-recap\",\"level\":2,\"text\":\"Plugins Recap\",\"anchor\":\"plugins-recap\"},{\"id\":\"how-to-install-a-plugin\",\"level\":3,\"text\":\"How to install a plugin\",\"anchor\":\"how-to-install-a-plugin\"},{\"id\":\"initialization\",\"level\":3,\"text\":\"Initialization\",\"anchor\":\"initialization\"},{\"id\":\"plugin-template\",\"level\":2,\"text\":\"Plugin Template\",\"anchor\":\"plugin-template\"},{\"id\":\"the-root-folder\",\"level\":3,\"text\":\"The root folder\",\"anchor\":\"the-root-folder\"},{\"id\":\"the-dev-folder\",\"level\":3,\"text\":\"The dev folder\",\"anchor\":\"the-dev-folder\"},{\"id\":\"testing\",\"level\":2,\"text\":\"Testing\",\"anchor\":\"testing\"},{\"id\":\"seeding-data\",\"level\":2,\"text\":\"Seeding data\",\"anchor\":\"seeding-data\"},{\"id\":\"building-a-plugin\",\"level\":2,\"text\":\"Building a Plugin\",\"anchor\":\"building-a-plugin\"},{\"id\":\"spread-syntax\",\"level\":3,\"text\":\"Spread syntax\",\"anchor\":\"spread-syntax\"},{\"id\":\"extending-functions\",\"level\":3,\"text\":\"Extending functions\",\"anchor\":\"extending-functions\"},{\"id\":\"types\",\"level\":2,\"text\":\"Types\",\"anchor\":\"types\"},{\"id\":\"best-practices\",\"level\":2,\"text\":\"Best practices\",\"anchor\":\"best-practices\"},{\"id\":\"providing-an-enable-disable-option\",\"level\":3,\"text\":\"Providing an enable / disable option\",\"anchor\":\"providing-an-enable-disable-option\"},{\"id\":\"include-tests-in-your-github-ci-workflow\",\"level\":3,\"text\":\"Include tests in your GitHub CI workflow\",\"anchor\":\"include-tests-in-your-github-ci-workflow\"},{\"id\":\"publish-your-finished-plugin-to-npm\",\"level\":3,\"text\":\"Publish your finished plugin to NPM\",\"anchor\":\"publish-your-finished-plugin-to-npm\"},{\"id\":\"add-payload-plugin-topic-tag\",\"level\":3,\"text\":\"Add payload-plugin topic tag\",\"anchor\":\"add-payload-plugin-topic-tag\"},{\"id\":\"use-semantic-versioning-semver\",\"level\":3,\"text\":\"Use Semantic Versioning (SemVer)\",\"anchor\":\"use-semantic-versioning-semver\"}],\"keywords\":\"plugins, template, config, configuration, extensions, custom, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Build Your Own\",\"order\":20,\"title\":\"Building Your Own Plugin\"},{\"slug\":\"seo\",\"content\":\"$99\",\"desc\":\"Manage SEO metadata from your Payload admin\",\"headings\":[{\"id\":\"core-features\",\"level\":2,\"text\":\"Core features\",\"anchor\":\"core-features\"},{\"id\":\"installation\",\"level\":2,\"text\":\"Installation\",\"anchor\":\"installation\"},{\"id\":\"basic-usage\",\"level\":2,\"text\":\"Basic Usage\",\"anchor\":\"basic-usage\"},{\"id\":\"options\",\"level\":3,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"direct-use-of-fields\",\"level\":2,\"text\":\"Direct use of fields\",\"anchor\":\"direct-use-of-fields\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"},{\"id\":\"examples\",\"level\":2,\"text\":\"Examples\",\"anchor\":\"examples\"},{\"id\":\"screenshots\",\"level\":2,\"text\":\"Screenshots\",\"anchor\":\"screenshots\"}],\"keywords\":\"plugins, seo, meta, search, engine, ranking, google\",\"label\":\"SEO\",\"order\":30,\"title\":\"SEO Plugin\"},{\"slug\":\"form-builder\",\"content\":\"$9a\",\"desc\":\"Easily build and manage forms from the Admin Panel. Send dynamic, personalized emails and even accept and process payments.\",\"headings\":[{\"id\":\"core-features\",\"level\":2,\"text\":\"Core Features\",\"anchor\":\"core-features\"},{\"id\":\"installation\",\"level\":2,\"text\":\"Installation\",\"anchor\":\"installation\"},{\"id\":\"basic-usage\",\"level\":2,\"text\":\"Basic Usage\",\"anchor\":\"basic-usage\"},{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"fields-option\",\"level\":3,\"text\":\"`fields` (option)\",\"anchor\":\"fields-option\"},{\"id\":\"redirectrelationships\",\"level\":3,\"text\":\"`redirectRelationships`\",\"anchor\":\"redirectrelationships\"},{\"id\":\"beforeemail\",\"level\":3,\"text\":\"`beforeEmail`\",\"anchor\":\"beforeemail\"},{\"id\":\"defaulttoemail\",\"level\":3,\"text\":\"`defaultToEmail`\",\"anchor\":\"defaulttoemail\"},{\"id\":\"formoverrides\",\"level\":3,\"text\":\"`formOverrides`\",\"anchor\":\"formoverrides\"},{\"id\":\"formsubmissionoverrides\",\"level\":3,\"text\":\"`formSubmissionOverrides`\",\"anchor\":\"formsubmissionoverrides\"},{\"id\":\"handlepayment\",\"level\":3,\"text\":\"`handlePayment`\",\"anchor\":\"handlepayment\"},{\"id\":\"fields\",\"level\":2,\"text\":\"Fields\",\"anchor\":\"fields\"},{\"id\":\"text\",\"level\":3,\"text\":\"Text\",\"anchor\":\"text\"},{\"id\":\"textarea\",\"level\":3,\"text\":\"Textarea\",\"anchor\":\"textarea\"},{\"id\":\"select\",\"level\":3,\"text\":\"Select\",\"anchor\":\"select\"},{\"id\":\"email-field\",\"level\":3,\"text\":\"Email (field)\",\"anchor\":\"email-field\"},{\"id\":\"state\",\"level\":3,\"text\":\"State\",\"anchor\":\"state\"},{\"id\":\"country\",\"level\":3,\"text\":\"Country\",\"anchor\":\"country\"},{\"id\":\"checkbox\",\"level\":3,\"text\":\"Checkbox\",\"anchor\":\"checkbox\"},{\"id\":\"number\",\"level\":3,\"text\":\"Number\",\"anchor\":\"number\"},{\"id\":\"message\",\"level\":3,\"text\":\"Message\",\"anchor\":\"message\"},{\"id\":\"payment\",\"level\":3,\"text\":\"Payment\",\"anchor\":\"payment\"},{\"id\":\"field-overrides\",\"level\":3,\"text\":\"Field Overrides\",\"anchor\":\"field-overrides\"},{\"id\":\"email\",\"level\":2,\"text\":\"Email\",\"anchor\":\"email\"},{\"id\":\"email-formatting\",\"level\":3,\"text\":\"Email formatting\",\"anchor\":\"email-formatting\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"},{\"id\":\"examples\",\"level\":2,\"text\":\"Examples\",\"anchor\":\"examples\"},{\"id\":\"troubleshooting\",\"level\":2,\"text\":\"Troubleshooting\",\"anchor\":\"troubleshooting\"},{\"id\":\"screenshots\",\"level\":2,\"text\":\"Screenshots\",\"anchor\":\"screenshots\"}],\"keywords\":\"plugins, plugin, form, forms, form builder\",\"label\":\"Form Builder\",\"order\":40,\"title\":\"Form Builder Plugin\"},{\"slug\":\"nested-docs\",\"content\":\"$9b\",\"desc\":\"Nested documents in a parent, child, and sibling relationship.\",\"headings\":[{\"id\":\"core-features\",\"level\":2,\"text\":\"Core features\",\"anchor\":\"core-features\"},{\"id\":\"installation\",\"level\":2,\"text\":\"Installation\",\"anchor\":\"installation\"},{\"id\":\"basic-usage\",\"level\":2,\"text\":\"Basic Usage\",\"anchor\":\"basic-usage\"},{\"id\":\"fields\",\"level\":3,\"text\":\"Fields\",\"anchor\":\"fields\"},{\"id\":\"options\",\"level\":3,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"overrides\",\"level\":2,\"text\":\"Overrides\",\"anchor\":\"overrides\"},{\"id\":\"localization\",\"level\":2,\"text\":\"Localization\",\"anchor\":\"localization\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"},{\"id\":\"examples\",\"level\":2,\"text\":\"Examples\",\"anchor\":\"examples\"}],\"keywords\":\"plugins, nested, documents, parent, child, sibling, relationship\",\"label\":\"Nested Docs\",\"order\":40,\"title\":\"Nested Docs Plugin\"},{\"slug\":\"redirects\",\"content\":\"$9c\",\"desc\":\"Automatically create redirects for your Payload application\",\"headings\":[{\"id\":\"core-features\",\"level\":2,\"text\":\"Core features\",\"anchor\":\"core-features\"},{\"id\":\"installation\",\"level\":2,\"text\":\"Installation\",\"anchor\":\"installation\"},{\"id\":\"basic-usage\",\"level\":2,\"text\":\"Basic Usage\",\"anchor\":\"basic-usage\"},{\"id\":\"options\",\"level\":3,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"},{\"id\":\"examples\",\"level\":2,\"text\":\"Examples\",\"anchor\":\"examples\"}],\"keywords\":\"plugins, redirects, redirect, plugin, payload, cms, seo, indexing, search, search engine\",\"label\":\"Redirects\",\"order\":40,\"title\":\"Redirects Plugin\"},{\"slug\":\"search\",\"content\":\"$9d\",\"desc\":\"Generates records of your documents that are extremely fast to search on.\",\"headings\":[{\"id\":\"core-features\",\"level\":2,\"text\":\"Core Features\",\"anchor\":\"core-features\"},{\"id\":\"installation\",\"level\":2,\"text\":\"Installation\",\"anchor\":\"installation\"},{\"id\":\"basic-usage\",\"level\":2,\"text\":\"Basic Usage\",\"anchor\":\"basic-usage\"},{\"id\":\"options\",\"level\":3,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"localize\",\"level\":3,\"text\":\"`localize`\",\"anchor\":\"localize\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"plugins, search, search plugin, search engine, search index, search results, search bar, search box, search field, search form, search input\",\"label\":\"Search\",\"order\":40,\"title\":\"Search Plugin\"},{\"slug\":\"sentry\",\"content\":\"$9e\",\"desc\":\"Integrate Sentry error tracking into your Payload application\",\"headings\":[{\"id\":\"what-is-sentry\",\"level\":2,\"text\":\"What is Sentry?\",\"anchor\":\"what-is-sentry\"},{\"id\":\"core-features\",\"level\":2,\"text\":\"Core Features\",\"anchor\":\"core-features\"},{\"id\":\"installation\",\"level\":2,\"text\":\"Installation\",\"anchor\":\"installation\"},{\"id\":\"sentry-for-nextjs-setup\",\"level\":2,\"text\":\"Sentry for Next.js setup\",\"anchor\":\"sentry-for-nextjs-setup\"},{\"id\":\"basic-usage\",\"level\":2,\"text\":\"Basic Usage\",\"anchor\":\"basic-usage\"},{\"id\":\"options\",\"level\":2,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"example\",\"level\":3,\"text\":\"Example\",\"anchor\":\"example\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"}],\"keywords\":\"plugins, sentry, error, tracking, monitoring, logging, bug, reporting, performance\",\"label\":\"Sentry\",\"order\":40,\"title\":\"Sentry Plugin\"},{\"slug\":\"stripe\",\"content\":\"$9f\",\"desc\":\"Easily accept payments with Stripe\",\"headings\":[{\"id\":\"core-features\",\"level\":2,\"text\":\"Core features\",\"anchor\":\"core-features\"},{\"id\":\"installation\",\"level\":2,\"text\":\"Installation\",\"anchor\":\"installation\"},{\"id\":\"basic-usage\",\"level\":2,\"text\":\"Basic Usage\",\"anchor\":\"basic-usage\"},{\"id\":\"options\",\"level\":3,\"text\":\"Options\",\"anchor\":\"options\"},{\"id\":\"endpoints\",\"level\":2,\"text\":\"Endpoints\",\"anchor\":\"endpoints\"},{\"id\":\"webhooks\",\"level\":2,\"text\":\"Webhooks\",\"anchor\":\"webhooks\"},{\"id\":\"node\",\"level\":2,\"text\":\"Node\",\"anchor\":\"node\"},{\"id\":\"sync\",\"level\":2,\"text\":\"Sync\",\"anchor\":\"sync\"},{\"id\":\"typescript\",\"level\":2,\"text\":\"TypeScript\",\"anchor\":\"typescript\"},{\"id\":\"examples\",\"level\":2,\"text\":\"Examples\",\"anchor\":\"examples\"}],\"keywords\":\"plugins, stripe, payments, ecommerce\",\"label\":\"Stripe\",\"order\":40,\"title\":\"Stripe Plugin\"}]},{\"slug\":\"Examples\",\"docs\":[{\"slug\":\"overview\",\"content\":\"$a0\",\"desc\":\"\",\"headings\":[],\"keywords\":\"example, examples, starter, boilerplate, template, templates\",\"label\":\"Overview\",\"order\":10,\"title\":\"Examples\"}]},{\"slug\":\"Integrations\",\"docs\":[{\"slug\":\"vercel-content-link\",\"content\":\"$a1\",\"desc\":\"Payload + Vercel Content Link allows yours editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it.\",\"headings\":[{\"id\":\"how-it-works\",\"level\":2,\"text\":\"How it works\",\"anchor\":\"how-it-works\"},{\"id\":\"getting-started\",\"level\":2,\"text\":\"Getting Started\",\"anchor\":\"getting-started\"},{\"id\":\"troubleshooting\",\"level\":2,\"text\":\"Troubleshooting\",\"anchor\":\"troubleshooting\"},{\"id\":\"date-fields\",\"level\":3,\"text\":\"Date Fields\",\"anchor\":\"date-fields\"},{\"id\":\"blocks-and-array-fields\",\"level\":3,\"text\":\"Blocks and array fields\",\"anchor\":\"blocks-and-array-fields\"}],\"keywords\":\"vercel, vercel content link, content link, visual editing, content source maps, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Vercel Content Link\",\"order\":10,\"title\":\"Vercel Content Link\"}]}]},{\"groupLabel\":\"Deployment\",\"topics\":[{\"slug\":\"Cloud\",\"docs\":[{\"slug\":\"creating-a-project\",\"content\":\"$a2\",\"desc\":\"Get started with Payload Cloud, a deployment solution specifically designed for Node + MongoDB applications.\",\"headings\":[{\"id\":\"starting-from-a-template\",\"level\":2,\"text\":\"Starting from a Template\",\"anchor\":\"starting-from-a-template\"},{\"id\":\"importing-from-an-existing-codebase\",\"level\":2,\"text\":\"Importing from an Existing Codebase\",\"anchor\":\"importing-from-an-existing-codebase\"}],\"keywords\":\"cloud, hosted, database, storage, email, deployment, serverless, node, mongodb, s3, aws, cloudflare, atlas, resend, payload, cms\",\"label\":\"Getting Started\",\"order\":10,\"title\":\"Getting Started\"},{\"slug\":\"configuration\",\"content\":\"$a3\",\"desc\":\"Quickly configure and deploy your Payload Cloud project in a few simple steps.\",\"headings\":[{\"id\":\"select-your-plan\",\"level\":2,\"text\":\"Select your plan\",\"anchor\":\"select-your-plan\"},{\"id\":\"project-details\",\"level\":2,\"text\":\"Project Details\",\"anchor\":\"project-details\"},{\"id\":\"build-settings\",\"level\":2,\"text\":\"Build Settings\",\"anchor\":\"build-settings\"},{\"id\":\"environment-variables\",\"level\":2,\"text\":\"Environment Variables\",\"anchor\":\"environment-variables\"},{\"id\":\"payment\",\"level\":2,\"text\":\"Payment\",\"anchor\":\"payment\"}],\"keywords\":\"configuration, config, settings, project, cloud, payload cloud, deploy, deployment\",\"label\":\"Configuration\",\"order\":20,\"title\":\"Project Configuration\"},{\"slug\":\"teams\",\"content\":\"$a4\",\"desc\":\"Manage your Payload Cloud team and billing settings.\",\"headings\":[{\"id\":\"members\",\"level\":2,\"text\":\"Members\",\"anchor\":\"members\"},{\"id\":\"adding-members\",\"level\":2,\"text\":\"Adding Members\",\"anchor\":\"adding-members\"},{\"id\":\"billing\",\"level\":2,\"text\":\"Billing\",\"anchor\":\"billing\"},{\"id\":\"subscriptions\",\"level\":2,\"text\":\"Subscriptions\",\"anchor\":\"subscriptions\"},{\"id\":\"invoices\",\"level\":2,\"text\":\"Invoices\",\"anchor\":\"invoices\"}],\"keywords\":\"team, teams, billing, subscription, payment, plan, plans, cloud, payload cloud\",\"label\":\"Teams\",\"order\":30,\"title\":\"Cloud Teams\"},{\"slug\":\"projects\",\"content\":\"$a5\",\"desc\":\"Manage your Payload Cloud projects.\",\"headings\":[{\"id\":\"overview\",\"level\":2,\"text\":\"Overview\",\"anchor\":\"overview\"},{\"id\":\"database\",\"level\":2,\"text\":\"Database\",\"anchor\":\"database\"},{\"id\":\"file-storage\",\"level\":2,\"text\":\"File Storage\",\"anchor\":\"file-storage\"},{\"id\":\"accessing-files-outside-of-payload-cloud\",\"level\":3,\"text\":\"Accessing Files Outside of Payload Cloud\",\"anchor\":\"accessing-files-outside-of-payload-cloud\"},{\"id\":\"build-settings\",\"level\":2,\"text\":\"Build Settings\",\"anchor\":\"build-settings\"},{\"id\":\"environment-variables\",\"level\":2,\"text\":\"Environment Variables\",\"anchor\":\"environment-variables\"},{\"id\":\"custom-domains\",\"level\":2,\"text\":\"Custom Domains\",\"anchor\":\"custom-domains\"},{\"id\":\"email\",\"level\":2,\"text\":\"Email\",\"anchor\":\"email\"},{\"id\":\"developing-locally\",\"level\":2,\"text\":\"Developing Locally\",\"anchor\":\"developing-locally\"},{\"id\":\"cloud-plugin\",\"level\":2,\"text\":\"Cloud Plugin\",\"anchor\":\"cloud-plugin\"}],\"keywords\":\"cloud, payload cloud, projects, project, overview, database, file storage, build settings, environment variables, custom domains, email, developing locally\",\"label\":\"Projects\",\"order\":40,\"title\":\"Cloud Projects\"}]},{\"slug\":\"Production\",\"docs\":[{\"slug\":\"deployment\",\"content\":\"$a6\",\"desc\":\"When your Payload based app is ready, tested, looking great, it is time to deploy. Learn how to deploy your app and what to consider before deployment.\",\"headings\":[{\"id\":\"basics\",\"level\":2,\"text\":\"Basics\",\"anchor\":\"basics\"},{\"id\":\"security\",\"level\":2,\"text\":\"Security\",\"anchor\":\"security\"},{\"id\":\"the-secret-key\",\"level\":3,\"text\":\"The Secret Key\",\"anchor\":\"the-secret-key\"},{\"id\":\"double-check-and-thoroughly-test-all-access-control\",\"level\":3,\"text\":\"Double-check and thoroughly test all Access Control\",\"anchor\":\"double-check-and-thoroughly-test-all-access-control\"},{\"id\":\"running-in-production\",\"level\":3,\"text\":\"Running in Production\",\"anchor\":\"running-in-production\"},{\"id\":\"secure-cookie-settings\",\"level\":3,\"text\":\"Secure Cookie Settings\",\"anchor\":\"secure-cookie-settings\"},{\"id\":\"preventing-api-abuse\",\"level\":3,\"text\":\"Preventing API Abuse\",\"anchor\":\"preventing-api-abuse\"},{\"id\":\"database\",\"level\":2,\"text\":\"Database\",\"anchor\":\"database\"},{\"id\":\"documentdb\",\"level\":3,\"text\":\"DocumentDB\",\"anchor\":\"documentdb\"},{\"id\":\"cosmosdb\",\"level\":3,\"text\":\"CosmosDB\",\"anchor\":\"cosmosdb\"},{\"id\":\"file-storage\",\"level\":2,\"text\":\"File storage\",\"anchor\":\"file-storage\"},{\"id\":\"persistent-vs-ephemeral-filesystems\",\"level\":3,\"text\":\"Persistent vs Ephemeral Filesystems\",\"anchor\":\"persistent-vs-ephemeral-filesystems\"},{\"id\":\"using-cloud-storage-providers\",\"level\":3,\"text\":\"Using cloud storage providers\",\"anchor\":\"using-cloud-storage-providers\"},{\"id\":\"docker\",\"level\":2,\"text\":\"Docker\",\"anchor\":\"docker\"},{\"id\":\"docker-compose\",\"level\":2,\"text\":\"Docker Compose\",\"anchor\":\"docker-compose\"}],\"keywords\":\"deployment, production, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Deployment\",\"order\":10,\"title\":\"Production Deployment\"},{\"slug\":\"preventing-abuse\",\"content\":\"$a7\",\"desc\":\"Payload has built-in security that can be configured to combat production API abuse such as limiting login attempts and IP requests.\",\"headings\":[{\"id\":\"introduction\",\"level\":2,\"text\":\"Introduction\",\"anchor\":\"introduction\"},{\"id\":\"limit-failed-login-attempts\",\"level\":2,\"text\":\"Limit Failed Login Attempts\",\"anchor\":\"limit-failed-login-attempts\"},{\"id\":\"max-depth\",\"level\":2,\"text\":\"Max Depth\",\"anchor\":\"max-depth\"},{\"id\":\"cross-site-request-forgery-csrf\",\"level\":2,\"text\":\"Cross-Site Request Forgery (CSRF)\",\"anchor\":\"cross-site-request-forgery-csrf\"},{\"id\":\"cross-origin-resource-sharing-cors\",\"level\":2,\"text\":\"Cross Origin Resource Sharing (CORS)\",\"anchor\":\"cross-origin-resource-sharing-cors\"},{\"id\":\"limiting-graphql-complexity\",\"level\":2,\"text\":\"Limiting GraphQL Complexity\",\"anchor\":\"limiting-graphql-complexity\"},{\"id\":\"malicious-file-uploads\",\"level\":2,\"text\":\"Malicious File Uploads\",\"anchor\":\"malicious-file-uploads\"}],\"keywords\":\"abuse, production, config, configuration, documentation, Content Management System, cms, headless, javascript, node, react, nextjs\",\"label\":\"Preventing Abuse\",\"order\":20,\"title\":\"Preventing Production API Abuse\"}]}]}],\"version\":\"$undefined\",\"groupIndex\":0,\"indexInGroup\":1,\"docIndex\":4}],[\"$\",\"div\",null,{\"aria-hidden\":true,\"className\":\"$undefined\"}],[\"$\",\"main\",null,{\"className\":\"cols-8 start-5 cols-m-8 start-m-1 RenderDocs_content__XnnWb\",\"children\":[[\"$\",\"$a8\",null,{\"fallback\":[\"$\",\"div\",null,{\"className\":\"RenderDocs_skeleton__bLcd_\",\"children\":[[\"$\",\"div\",null,{\"className\":\"RenderDocs_skeletonTitle__PsHB6\"}],[\"$\",\"div\",null,{\"className\":\"RenderDocs_skeletonContent__pWaHO\"}]]}],\"children\":[\"$undefined\",[\"$\",\"h1\",null,{\"className\":\"RenderDocs_title__h1NVD\",\"children\":\"Localization\"}],[\"$\",\"div\",null,{\"className\":\"RenderDocs_mdx__C8FvH\",\"children\":\"$La9\"}]]}],[\"$\",\"$Laa\",null,{\"className\":\"RenderDocs_next__wMNfO RenderDocs_hasRelatedThreads__kdwC3\",\"data-algolia-no-crawl\":true,\"href\":\"/docs/configuration/environment-vars\",\"prefetch\":false,\"children\":[[\"$\",\"div\",null,{\"className\":\"RenderDocs_nextLabel__xEloF\",\"children\":[\"Next \",[\"$\",\"svg\",null,{\"width\":\"100%\",\"height\":\"100%\",\"viewBox\":\"0 0 13 13\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"icons_icon__6EVpU\",\"style\":{\"transform\":\"$undefined\"},\"children\":[[\"$\",\"path\",null,{\"d\":\"M1 12L12.5 0.499965\",\"className\":\"icons_stroke__FrZ8d\"}],[\"$\",\"path\",null,{\"d\":\"M1 0.5H12.5V12\",\"className\":\"icons_stroke__FrZ8d\"}]]}]]}],[\"$\",\"h3\",null,{\"children\":\"Environment Variables\"}],[\"$\",\"div\",null,{\"aria-hidden\":\"true\",\"className\":\"BackgroundScanline_wrapper__TTtqa\",\"style\":\"$undefined\",\"children\":[[\"$\",\"div\",null,{\"className\":\"BackgroundScanline_backgroundScanline__iS5AQ\"}],[[\"$\",\"svg\",null,{\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"BackgroundScanline_crosshair__Au3az BackgroundScanline_crosshairTopLeft__MzINp crosshair icons_icon__6EVpU icons_large__tRw9w\",\"width\":\"20\",\"height\":\"21\",\"viewBox\":\"0 0 20 21\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"children\":[[\"$\",\"path\",null,{\"d\":\"M10 0.332031V20.332\"}],[\"$\",\"path\",null,{\"d\":\"M0 10.332L20 10.332\"}]]}],[\"$\",\"svg\",null,{\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"BackgroundScanline_crosshair__Au3az BackgroundScanline_crosshairBottomLeft__vRreZ crosshair icons_icon__6EVpU icons_large__tRw9w\",\"width\":\"20\",\"height\":\"21\",\"viewBox\":\"0 0 20 21\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"children\":[[\"$\",\"path\",null,{\"d\":\"M10 0.332031V20.332\"}],[\"$\",\"path\",null,{\"d\":\"M0 10.332L20 10.332\"}]]}],[\"$\",\"svg\",null,{\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"BackgroundScanline_crosshair__Au3az BackgroundScanline_crosshairTopRight__c1rwU crosshair icons_icon__6EVpU icons_large__tRw9w\",\"width\":\"20\",\"height\":\"21\",\"viewBox\":\"0 0 20 21\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"children\":[[\"$\",\"path\",null,{\"d\":\"M10 0.332031V20.332\"}],[\"$\",\"path\",null,{\"d\":\"M0 10.332L20 10.332\"}]]}],[\"$\",\"svg\",null,{\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"BackgroundScanline_crosshair__Au3az BackgroundScanline_crosshairBottomRight__ISe0a crosshair icons_icon__6EVpU icons_large__tRw9w\",\"width\":\"20\",\"height\":\"21\",\"viewBox\":\"0 0 20 21\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"children\":[[\"$\",\"path\",null,{\"d\":\"M10 0.332031V20.332\"}],[\"$\",\"path\",null,{\"d\":\"M0 10.332L20 10.332\"}]]}]]]}]]}],[\"$\",\"div\",null,{\"className\":\"RelatedHelpList_relatedHelpList__5N3gz\",\"children\":[[\"$\",\"div\",null,{\"className\":\"RelatedHelpList_titleWrapper__2VjfE\",\"children\":[\"$\",\"h4\",null,{\"className\":\"RelatedHelpList_title__IkeKR\",\"children\":\"Related Help Topics\"}]}],[\"$\",\"ul\",null,{\"className\":\"$undefined\",\"children\":[[\"$\",\"li\",\"0\",{\"children\":[[\"$\",\"svg\",null,{\"className\":\"RelatedHelpList_itemMarker__uRSIP\",\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"0 0 32 32\",\"fill\":\"none\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"children\":[[\"$\",\"path\",null,{\"d\":\"M11.2129 18.8555C11.5352 19.1025 11.9258 19.248 12.3457 19.248C13.4512 19.248 14.3066 18.2744 14.3242 17.0752C14.3438 15.877 13.4551 14.8936 12.3418 14.8936C11.2285 14.8936 10.3633 15.877 10.3633 17.0752C10.3633 17.8115 10.7012 18.4629 11.2129 18.8555Z\",\"fill\":\"currentColor\"}],[\"$\",\"path\",null,{\"d\":\"M17.6758 17.0752C17.6758 18.2744 18.5684 19.248 19.6543 19.248C20.7598 19.248 21.6133 18.2744 21.6328 17.0752C21.6523 15.877 20.7695 14.8936 19.6543 14.8936C18.541 14.8936 17.6758 15.877 17.6758 17.0752Z\",\"fill\":\"currentColor\"}],[\"$\",\"path\",null,{\"fillRule\":\"evenodd\",\"clipRule\":\"evenodd\",\"d\":\"$ab\",\"fill\":\"currentColor\"}]]}],false,[\"$\",\"$Laa\",null,{\"href\":\"/community-help/discord/localize-errors\",\"children\":\"Localize Errors\"}]]}],[\"$\",\"li\",\"1\",{\"children\":[[\"$\",\"svg\",null,{\"className\":\"RelatedHelpList_itemMarker__uRSIP\",\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"0 0 32 32\",\"fill\":\"none\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"children\":[[\"$\",\"path\",null,{\"d\":\"M11.2129 18.8555C11.5352 19.1025 11.9258 19.248 12.3457 19.248C13.4512 19.248 14.3066 18.2744 14.3242 17.0752C14.3438 15.877 13.4551 14.8936 12.3418 14.8936C11.2285 14.8936 10.3633 15.877 10.3633 17.0752C10.3633 17.8115 10.7012 18.4629 11.2129 18.8555Z\",\"fill\":\"currentColor\"}],[\"$\",\"path\",null,{\"d\":\"M17.6758 17.0752C17.6758 18.2744 18.5684 19.248 19.6543 19.248C20.7598 19.248 21.6133 18.2744 21.6328 17.0752C21.6523 15.877 20.7695 14.8936 19.6543 14.8936C18.541 14.8936 17.6758 15.877 17.6758 17.0752Z\",\"fill\":\"currentColor\"}],[\"$\",\"path\",null,{\"fillRule\":\"evenodd\",\"clipRule\":\"evenodd\",\"d\":\"$ac\",\"fill\":\"currentColor\"}]]}],false,[\"$\",\"$Laa\",null,{\"href\":\"/community-help/discord/query-specific-collection-with-all-locales\",\"children\":\"Query specific collection with all locales\"}]]}],[\"$\",\"li\",\"2\",{\"children\":[[\"$\",\"svg\",null,{\"className\":\"RelatedHelpList_itemMarker__uRSIP\",\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"0 0 32 32\",\"fill\":\"none\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"children\":[[\"$\",\"path\",null,{\"d\":\"M11.2129 18.8555C11.5352 19.1025 11.9258 19.248 12.3457 19.248C13.4512 19.248 14.3066 18.2744 14.3242 17.0752C14.3438 15.877 13.4551 14.8936 12.3418 14.8936C11.2285 14.8936 10.3633 15.877 10.3633 17.0752C10.3633 17.8115 10.7012 18.4629 11.2129 18.8555Z\",\"fill\":\"currentColor\"}],[\"$\",\"path\",null,{\"d\":\"M17.6758 17.0752C17.6758 18.2744 18.5684 19.248 19.6543 19.248C20.7598 19.248 21.6133 18.2744 21.6328 17.0752C21.6523 15.877 20.7695 14.8936 19.6543 14.8936C18.541 14.8936 17.6758 15.877 17.6758 17.0752Z\",\"fill\":\"currentColor\"}],[\"$\",\"path\",null,{\"fillRule\":\"evenodd\",\"clipRule\":\"evenodd\",\"d\":\"$ad\",\"fill\":\"currentColor\"}]]}],false,[\"$\",\"$Laa\",null,{\"href\":\"/community-help/discord/would-moving-localisation-to-its-own-collections-similar-to-versions-reduce-fear-of-migration\",\"children\":\"Would moving localisation to its own collections similar to versions reduce fear of migration?\"}]]}]]}]]}]]}],[\"$\",\"aside\",null,{\"className\":\"cols-3 start-14 RenderDocs_aside__2YzK7\",\"children\":[\"$\",\"div\",null,{\"className\":\"RenderDocs_asideStickyContent__6tCbZ\",\"children\":[[\"$\",\"$Lae\",null,{\"initialVersion\":\"current\"}],[\"$\",\"$Laf\",null,{\"headings\":\"$20:props:children:props:children:0:props:children:0:props:topics:0:topics:1:docs:4:headings\"}],[\"$\",\"div\",null,{\"className\":\"RenderDocs_discordGitWrap__MRSs_\",\"children\":[\"$\",\"div\",null,{\"className\":\"DiscordGitCTA_ctaWrap__tS4HQ\",\"children\":[[\"$\",\"$Laa\",null,{\"className\":\"DiscordGitCTA_cta__TpUYy\",\"href\":\"https://github.com/payloadcms/payload\",\"target\":\"_blank\",\"children\":[[\"$\",\"div\",null,{\"className\":\"DiscordGitCTA_message__Lq_Nt\",\"children\":[\"Star on GitHub\",[\"$\",\"svg\",null,{\"width\":\"100%\",\"height\":\"100%\",\"viewBox\":\"0 0 13 13\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"DiscordGitCTA_arrow__4uqoA icons_icon__6EVpU\",\"style\":{\"transform\":\"$undefined\"},\"children\":[[\"$\",\"path\",null,{\"d\":\"M1 12L12.5 0.499965\",\"className\":\"icons_stroke__FrZ8d\"}],[\"$\",\"path\",null,{\"d\":\"M1 0.5H12.5V12\",\"className\":\"icons_stroke__FrZ8d\"}]]}]]}],[\"$\",\"div\",null,{\"className\":\"DiscordGitCTA_gitButton__QBm7t\",\"children\":[\"$\",\"$Lb0\",null,{\"className\":\"$undefined\"}]}]]}],[\"$\",\"$Laa\",null,{\"aria-label\":\"Chat on Discord\",\"className\":\"DiscordGitCTA_cta__TpUYy\",\"href\":\"https://discord.gg/FSn5QRdsbC\",\"target\":\"_blank\",\"children\":[[\"$\",\"div\",null,{\"className\":\"DiscordGitCTA_message__Lq_Nt\",\"children\":[\"Chat on Discord\",[\"$\",\"svg\",null,{\"width\":\"100%\",\"height\":\"100%\",\"viewBox\":\"0 0 13 13\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"DiscordGitCTA_arrow__4uqoA icons_icon__6EVpU\",\"style\":{\"transform\":\"$undefined\"},\"children\":[[\"$\",\"path\",null,{\"d\":\"M1 12L12.5 0.499965\",\"className\":\"icons_stroke__FrZ8d\"}],[\"$\",\"path\",null,{\"d\":\"M1 0.5H12.5V12\",\"className\":\"icons_stroke__FrZ8d\"}]]}]]}],[\"$\",\"div\",null,{\"className\":\"DiscordGitCTA_discordButton__k9tbH\",\"children\":[\"$\",\"$Lb1\",null,{\"className\":\"$undefined\"}]}]]}],false]}]}],\"$Lb2\"]}]}]]}],[\"$\",\"div\",null,{\"aria-hidden\":\"true\",\"className\":\"BackgroundGrid_backgroundGrid__oXHXt grid BackgroundGrid_wideGrid__CNoDS\",\"style\":{\"zIndex\":-1},\"children\":[[\"$\",\"div\",\"0\",{\"className\":\"BackgroundGrid_column__Q_qjH cols-4\",\"style\":{}}],[\"$\",\"div\",\"1\",{\"className\":\"BackgroundGrid_column__Q_qjH cols-4\",\"style\":{}}],[\"$\",\"div\",\"2\",{\"className\":\"BackgroundGrid_column__Q_qjH cols-4\",\"style\":{}}],[\"$\",\"div\",\"3\",{\"className\":\"BackgroundGrid_column__Q_qjH cols-4\",\"style\":{}}]]}]]}]}]\n"])</script><script>self.__next_f.push([1,"b3:I[9569,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"default\"]\nb4:I[26446,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"default\"]\nb5:I[77869,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks"])</script><script>self.__next_f.push([1,"/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"default\"]\nb6:I[1227,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"default\"]\nb7:I[77468,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\""])</script><script>self.__next_f.push([1,"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"default\"]\nb8:I[51567,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"DrawerToggler\"]\nb9:I[51567,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"static/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"Drawer\"]\nba:I[99624,[\"6658\",\"static/chunks/6658-fb794c29f5a98524.js\",\"3966\",\"static/chunks/3966-a2a2bbc25ead1892.js\",\"8640\",\"static/chunks/8640-19363ff25b5bc926.js\",\"4800\",\"static/chunks/4800-03b4f9e37b2b6c75.js\",\"7103\",\"static/chunks/7103-67c634765b48b90f.js\",\"4615\",\"s"])</script><script>self.__next_f.push([1,"tatic/chunks/4615-52c1ec00e8f8c199.js\",\"6856\",\"static/chunks/6856-746769b4a7fc3a5e.js\",\"2033\",\"static/chunks/2033-d91a980ce0ad8789.js\",\"5179\",\"static/chunks/5179-acc9e6e828caa86a.js\",\"8819\",\"static/chunks/8819-d79276df4be00a63.js\",\"1092\",\"static/chunks/1092-1aacfd2ddddca0f7.js\",\"6873\",\"static/chunks/6873-4a30b3dc38bee630.js\",\"730\",\"static/chunks/730-f59bf88ee3e8bdc4.js\",\"5363\",\"static/chunks/5363-255b3f37e9cb9dca.js\",\"6429\",\"static/chunks/6429-416b9abc3627a940.js\",\"2205\",\"static/chunks/app/(frontend)/(pages)/docs/%5Btopic%5D/%5Bdoc%5D/page-c294b5b3a8ae07ed.js\"],\"CMSForm\"]\n"])</script><script>self.__next_f.push([1,"a9:[[\"$\",\"p\",null,{\"children\":[\"Localization is one of the most important features of a modern CMS. It allows you to manage content in multiple languages, then serve it to your users based on their requested language. This is similar to \",[\"$\",\"a\",null,{\"href\":\"./i18n\",\"children\":\"I18n\"}],\", but instead of managing translations for your application's interface, you are managing translations for the data itself.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"With Localization, you can begin to serve personalized content to your users based on their specific language preferences, such as a multilingual website or multi-site application. There are no limits to the number of locales you can add to your Payload project.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"To configure Localization, use the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"localization\"}]}],\" key in your \",[\"$\",\"a\",null,{\"href\":\"./overview\",\"children\":\"Payload Config\"}],\":\"]}],\"\\n\",[\"$\",\"$Lb3\",null,{\"parentClassName\":\"Code_code__v2XQZ\",\"disableMinHeight\":true,\"children\":\"import { buildConfig } from 'payload'\\n\\nexport default buildConfig({\\n // ...\\n localization: { // highlight-line\\n // ...\\n },\\n})\"}],\"\\n\",[\"$\",\"$Lb4\",null,{\"children\":\"Config Options\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Add the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"localization\"}]}],\" property to your Payload Config to enable Localization project-wide. You'll need to provide a list of all locales that you'd like to support as well as set a few other options.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"To configure locales, use the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"localization.locales\"}]}],\" property in your \",[\"$\",\"a\",null,{\"href\":\"./overview\",\"children\":\"Payload Config\"}],\":\"]}],\"\\n\",[\"$\",\"$Lb3\",null,{\"parentClassName\":\"Code_code__v2XQZ\",\"disableMinHeight\":true,\"children\":\"import { buildConfig } from 'payload'\\n\\nexport default buildConfig({\\n // ...\\n localization: {\\n locales: ['en', 'es', 'de'], // required\\n defaultLocale: 'en', // required\\n },\\n})\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"You can also define locales using \",[\"$\",\"a\",null,{\"href\":\"#locale-object\",\"children\":\"full configuration objects\"}],\":\"]}],\"\\n\",[\"$\",\"$Lb3\",null,{\"parentClassName\":\"Code_code__v2XQZ\",\"disableMinHeight\":true,\"children\":\"import { buildConfig } from 'payload'\\n\\nexport default buildConfig({\\n collections: [\\n // collections go here\\n ],\\n localization: {\\n locales: [\\n {\\n label: 'English',\\n code: 'en',\\n },\\n {\\n label: 'Arabic',\\n code: 'ar',\\n // opt-in to setting default text-alignment on Input fields to rtl (right-to-left)\\n // when current locale is rtl\\n rtl: true,\\n },\\n ],\\n defaultLocale: 'en', // required\\n fallback: true, // defaults to true\\n },\\n})\"}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"Banner_banner___cbsO banner Banner_success__7k55f\",\"children\":[\"$undefined\",[\"$\",\"div\",null,{\"className\":\"Banner_children__fnU4H\",\"children\":[\"$\",\"p\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":\"Tip:\"}],\"\\nLocalization works very well alongside \",[\"$\",\"a\",null,{\"href\":\"../configuration/i18n\",\"children\":\"I18n\"}],\".\"]}]}]]}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"The following options are available:\"}],\"\\n\",[\"$\",\"$Lb5\",null,{\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"children\":[[\"$\",\"th\",null,{\"children\":\"Option\"}],[\"$\",\"th\",null,{\"children\":\"Description\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",null,{\"children\":[[\"$\",\"td\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"locales\"}]}]}]}],[\"$\",\"td\",null,{\"children\":[\"Array of all the languages that you would like to support. \",[\"$\",\"a\",null,{\"href\":\"#locales\",\"children\":\"More details\"}]]}]]}],[\"$\",\"tr\",null,{\"children\":[[\"$\",\"td\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"defaultLocale\"}]}]}]}],[\"$\",\"td\",null,{\"children\":\"Required string that matches one of the locale codes from the array provided. By default, if no locale is specified, documents will be returned in this locale.\"}]]}],[\"$\",\"tr\",null,{\"children\":[[\"$\",\"td\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"fallback\"}]}]}]}],[\"$\",\"td\",null,{\"children\":\"Boolean enabling \\\"fallback\\\" locale functionality. If a document is requested in a locale, but a field does not have a localized value corresponding to the requested locale, then if this property is enabled, the document will automatically fall back to the fallback locale value. If this property is not enabled, the value will not be populated unless a fallback is explicitly provided in the request. True by default.\"}]]}]]}]]}],\"\\n\",[\"$\",\"$Lb6\",null,{\"children\":\"Locales\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The locales array is a list of all the languages that you would like to support. This can be strings for each language code, or \",[\"$\",\"a\",null,{\"href\":\"#locale-object\",\"children\":\"full configuration objects\"}],\" for more advanced options.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The locale codes do not need to be in any specific format. It's up to you to define how to represent your locales. Common patterns are to use two-letter ISO 639 language codes or four-letter language and country codes (ISO 3166‑1) such as \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"en-US\"}]}],\", \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"en-UK\"}]}],\", \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"es-MX\"}]}],\", etc.\"]}],\"\\n\",[\"$\",\"$Lb7\",null,{\"children\":\"Locale Object\"}],\"\\n\",[\"$\",\"$Lb5\",null,{\"children\":[[\"$\",\"thead\",null,{\"children\":[\"$\",\"tr\",null,{\"children\":[[\"$\",\"th\",null,{\"children\":\"Option\"}],[\"$\",\"th\",null,{\"children\":\"Description\"}]]}]}],[\"$\",\"tbody\",null,{\"children\":[[\"$\",\"tr\",null,{\"children\":[[\"$\",\"td\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"code\"}]}]}],\" *\"]}],[\"$\",\"td\",null,{\"children\":[\"Unique code to identify the language throughout the APIs for \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"locale\"}]}],\" and \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"fallbackLocale\"}]}]]}]]}],[\"$\",\"tr\",null,{\"children\":[[\"$\",\"td\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"label\"}]}]}]}],[\"$\",\"td\",null,{\"children\":\"A string to use for the selector when choosing a language, or an object keyed on the i18n keys for different languages in use.\"}]]}],[\"$\",\"tr\",null,{\"children\":[[\"$\",\"td\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"rtl\"}]}]}]}],[\"$\",\"td\",null,{\"children\":\"A boolean that when true will make the admin UI display in Right-To-Left.\"}]]}],[\"$\",\"tr\",null,{\"children\":[[\"$\",\"td\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"fallbackLocale\"}]}]}]}],[\"$\",\"td\",null,{\"children\":\"The code for this language to fallback to when properties of a document are not present.\"}]]}]]}]]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"$\",\"em\",null,{\"children\":\"* An asterisk denotes that a property is required.\"}]}],\"\\n\",[\"$\",\"$Lb4\",null,{\"children\":\"Field Localization\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Payload Localization works on a \",[\"$\",\"strong\",null,{\"children\":\"field\"}],\" level—not a document level. In addition to configuring the base Payload Config to support Localization, you need to specify each field that you would like to localize.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":\"Here is an example of how to enable Localization for a field:\"}]}],\"\\n\",[\"$\",\"$Lb3\",null,{\"parentClassName\":\"Code_code__v2XQZ\",\"disableMinHeight\":true,\"children\":\"{\\n name: 'title',\\n type: 'text',\\n // highlight-start\\n localized: true,\\n // highlight-end\\n}\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"With the above configuration, the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"title\"}]}],\" field will now be saved in the database as an object of all locales instead of a single string.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"All field types with a \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"name\"}]}],\" property support the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"localized\"}]}],\" property—even the more complex field types like \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"array\"}]}],\"s and \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"block\"}]}],\"s.\"]}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"Banner_banner___cbsO banner Banner_info__uBu0x\",\"children\":[\"$undefined\",[\"$\",\"div\",null,{\"className\":\"Banner_children__fnU4H\",\"children\":[\"$\",\"p\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":\"Note:\"}],\"\\nEnabling Localization for field types that support nested fields will automatically create\\nlocalized \\\"sets\\\" of all fields contained within the field. For example, if you have a page layout\\nusing a blocks field type, you have the choice of either localizing the full layout, by enabling\\nLocalization on the top-level blocks field, or only certain fields within the layout.\"]}]}]]}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"Banner_banner___cbsO banner Banner_warning__t8CN8\",\"children\":[\"$undefined\",[\"$\",\"div\",null,{\"className\":\"Banner_children__fnU4H\",\"children\":[\"$\",\"p\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":\"Important:\"}],\"\\nWhen converting an existing field to or from \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"localized: true\"}]}],\" the data structure in the document\\nwill change for this field and so existing data for this field will be lost. Before changing the\\nLocalization setting on fields with existing data, you may need to consider a field migration\\nstrategy.\"]}]}]]}],\"\\n\",[\"$\",\"$Lb4\",null,{\"children\":\"Retrieving Localized Docs\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"When retrieving documents, you can specify which locale you'd like to receive as well as which fallback locale should be\\nused.\"}],\"\\n\",[\"$\",\"$Lb7\",null,{\"children\":\"REST API\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":\"REST API locale functionality relies on URL query parameters.\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"?locale=\"}]}]}]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Specify your desired locale by providing the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"locale\"}]}],\" query parameter directly in the endpoint URL.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"?fallback-locale=\"}]}]}]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"Specify fallback locale to be used by providing the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"fallback-locale\"}]}],\" query parameter. This can be provided as either a\\nvalid locale as provided to your base Payload Config, or \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"'null'\"}]}],\", \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"'false'\"}]}],\", or \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"'none'\"}]}],\" to disable falling back.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":\"Example:\"}]}],\"\\n\",[\"$\",\"$Lb3\",null,{\"parentClassName\":\"Code_code__v2XQZ\",\"disableMinHeight\":true,\"children\":\"fetch('https://localhost:3000/api/pages?locale=es\u0026fallback-locale=none');\"}],\"\\n\",[\"$\",\"$Lb7\",null,{\"children\":\"GraphQL API\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"In the GraphQL API, you can specify \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"locale\"}]}],\" and \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"fallbackLocale\"}]}],\" args to all relevant queries and mutations.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"locale\"}]}],\" arg will only accept valid locales, but locales will be formatted automatically as valid GraphQL enum\\nvalues (dashes or special characters will be converted to underscores, spaces will be removed, etc.). If you are curious\\nto see how locales are auto-formatted, you can use the \",[\"$\",\"a\",null,{\"href\":\"../graphql/overview#graphql-playground\",\"children\":\"GraphQL playground\"}],\".\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"The \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"fallbackLocale\"}]}],\" arg will accept valid locales as well as \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"none\"}]}],\" to disable falling back.\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":\"Example:\"}]}],\"\\n\",[\"$\",\"$Lb3\",null,{\"parentClassName\":\"Code_code__v2XQZ\",\"disableMinHeight\":true,\"children\":\"query {\\n Posts(locale: de, fallbackLocale: none) {\\n docs {\\n title\\n }\\n }\\n}\"}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"Banner_banner___cbsO banner Banner_default__NT5rD\",\"children\":[\"$undefined\",[\"$\",\"div\",null,{\"className\":\"Banner_children__fnU4H\",\"children\":[\"$\",\"p\",null,{\"children\":\"In GraphQL, specifying the locale at the top level of a query will automatically apply it\\nthroughout all nested relationship fields. You can override this behavior by re-specifying locale\\narguments in nested related document queries.\"}]}]]}],\"\\n\",[\"$\",\"$Lb7\",null,{\"children\":\"Local API\"}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"You can specify \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"locale\"}]}],\" as well as \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"fallbackLocale\"}]}],\" within the Local API as well as properties on the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"options\"}]}],\"\\nargument. The \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"locale\"}]}],\" property will accept any valid locale, and the \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"fallbackLocale\"}]}],\" property will accept any valid\\nlocale as well as \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"'null'\"}]}],\", \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"'false'\"}]}],\", \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"false\"}]}],\", and \",[\"$\",\"span\",null,{\"className\":\"InlineCode_wrap__vw_Bo\",\"children\":[\"$\",\"code\",null,{\"children\":\"'none'\"}]}],\".\"]}],\"\\n\",[\"$\",\"p\",null,{\"children\":[\"$\",\"strong\",null,{\"children\":\"Example:\"}]}],\"\\n\",[\"$\",\"$Lb3\",null,{\"parentClassName\":\"Code_code__v2XQZ\",\"disableMinHeight\":true,\"children\":\"const posts = await payload.find({\\n collection: 'posts',\\n locale: 'es',\\n fallbackLocale: false,\\n})\"}],\"\\n\",[\"$\",\"div\",null,{\"className\":\"Banner_banner___cbsO banner Banner_success__7k55f\",\"children\":[\"$undefined\",[\"$\",\"div\",null,{\"className\":\"Banner_children__fnU4H\",\"children\":[\"$\",\"p\",null,{\"children\":[[\"$\",\"strong\",null,{\"children\":\"Tip:\"}],\"\\nThe REST and Local APIs can return all Localization data in one request by passing 'all' or '*' as\\nthe \",[\"$\",\"strong\",null,{\"children\":\"locale\"}],\" parameter. The response will be structured so that field values come\\nback as the full objects keyed for each locale instead of the single, translated value.\"]}]}]]}]]\n"])</script><script>self.__next_f.push([1,"b2:[\"$\",\"div\",null,{\"className\":\"Feedback_feedbackWrapper__tRn5l\",\"children\":[[\"$\",\"$Laa\",null,{\"className\":\"Feedback_gitHubLink__XEOgg\",\"href\":\"https://github.com/payloadcms/payload/blob/main/docs/configuration/localization.mdx\",\"target\":\"_blank\",\"children\":[\"Edit this page on GitHub \",[\"$\",\"svg\",null,{\"width\":\"100%\",\"height\":\"100%\",\"viewBox\":\"0 0 13 13\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"Feedback_arrow__oXpS9 icons_icon__6EVpU\",\"style\":{\"transform\":\"$undefined\"},\"children\":[[\"$\",\"path\",null,{\"d\":\"M1 12L12.5 0.499965\",\"className\":\"icons_stroke__FrZ8d\"}],[\"$\",\"path\",null,{\"d\":\"M1 0.5H12.5V12\",\"className\":\"icons_stroke__FrZ8d\"}]]}]]}],[[\"$\",\"$Lb8\",null,{\"className\":\"Feedback_drawerButton__h47Sa\",\"slug\":\"feedbackDrawer\",\"children\":[\"Leave feedback \",[\"$\",\"svg\",null,{\"width\":\"100%\",\"height\":\"100%\",\"viewBox\":\"0 0 13 13\",\"xmlns\":\"http://www.w3.org/2000/svg\",\"className\":\"Feedback_arrow__oXpS9 icons_icon__6EVpU\",\"style\":{\"transform\":\"$undefined\"},\"children\":[[\"$\",\"path\",null,{\"d\":\"M1 12L12.5 0.499965\",\"className\":\"icons_stroke__FrZ8d\"}],[\"$\",\"path\",null,{\"d\":\"M1 0.5H12.5V12\",\"className\":\"icons_stroke__FrZ8d\"}]]}]]}],[\"$\",\"$Lb9\",null,{\"size\":\"s\",\"slug\":\"feedbackDrawer\",\"title\":\"Documentation Feedback\",\"children\":[\"$\",\"$Lba\",null,{\"form\":{\"id\":\"673cdf6938fa9c6a3bc8432f\",\"title\":\"Feedback\",\"fields\":[{\"blockType\":\"message\",\"message\":{\"root\":{\"children\":[{\"children\":[{\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"Share suggestions and ideas about how we can make improvements to our documentation.\",\"type\":\"text\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"paragraph\",\"version\":1,\"textFormat\":0,\"textStyle\":\"\"}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}},\"id\":\"673cdf6963954c00039f2392\"},{\"blockType\":\"textarea\",\"name\":\"message\",\"label\":\"Feedback suggestions\",\"required\":true,\"id\":\"673cdf6963954c00039f2393\"}],\"submitButtonLabel\":\"Submit\",\"confirmationType\":\"message\",\"confirmationMessage\":{\"root\":{\"children\":[{\"children\":[{\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"Thanks for you feedback!\",\"type\":\"text\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"paragraph\",\"version\":1,\"textFormat\":0,\"textStyle\":\"\"}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}},\"redirect\":{},\"emails\":[{\"emailTo\":\"\\\"Payload\\\" \u003cinfo@payloadcms.com\u003e\",\"emailFrom\":\"\\\"Payload Feedback\\\" \u003cinfo@payloadcms.com\u003e\",\"subject\":\"Documentation Feedback\",\"message\":{\"root\":{\"children\":[{\"children\":[{\"detail\":0,\"format\":0,\"mode\":\"normal\",\"style\":\"\",\"text\":\"{{message}}\",\"type\":\"text\",\"version\":1}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"paragraph\",\"version\":1,\"textFormat\":0,\"textStyle\":\"\"}],\"direction\":\"ltr\",\"format\":\"\",\"indent\":0,\"type\":\"root\",\"version\":1}},\"id\":\"673cdf6963954c00039f2394\"}],\"createdAt\":\"2024-11-19T18:56:41.394Z\",\"updatedAt\":\"2024-11-22T19:00:41.961Z\"}}]}]]]}]\n"])</script></body></html>