Alessio Gravili 6e203db33c feat(live-preview): client-side live preview: simplify population, support hooks and lexical block population (#13619)
Alternative solution to
https://github.com/payloadcms/payload/pull/11104. Big thanks to
@andershermansen and @GermanJablo for kickstarting work on a solution
and bringing this to our attention. This PR copies over the live-preview
test suite example from his PR.

Fixes https://github.com/payloadcms/payload/issues/5285,
https://github.com/payloadcms/payload/issues/6071 and
https://github.com/payloadcms/payload/issues/8277. Potentially fixes
#11801

This PR completely gets rid of our client-side live preview field
traversal + population and all logic related to it, and instead lets the
findByID endpoint handle it.

The data sent through the live preview message event is now passed to
findByID via the newly added `data` attribute. The findByID endpoint
will then use this data and run hooks on it (which run population),
instead of fetching the data from the database.

This new API basically behaves like a `/api/populate?data=` endpoint,
with the benefit that it runs all the hooks. Another use-case for it
will be rendering lexical data. Sometimes you may only have unpopulated
data available. This functionality allows you to then populate the
lexical portion of it on-the-fly, so that you can properly render it to
JSX while displaying images.

## Benefits
- a lot less code to maintain. No duplicative population logic
- much faster - one single API request instead of one request per
relationship to populate
- all payload features are now correctly supported (population and
hooks)
- since hooks are now running for client-side live preview, this means
the `lexicalHTML` field is now supported! This was a long-running issue
- this fixes a lot of population inconsistencies that we previously did
not know of. For example, it previously populated lexical and slate
relationships even if the data was saved in an incorrect format

## [Method Override
(POST)](https://payloadcms.com/docs/rest-api/overview#using-method-override-post)
change

The population request to the findByID endpoint is sent as a post
request, so that we can pass through the `data` without having to
squeeze it into the url params. To do that, it uses the
`X-Payload-HTTP-Method-Override` header.

Previously, this functionality still expected the data to be sent
through as URL search params - just passed to the body instead of the
URL. In this PR, I made it possible to pass it as JSON instead. This
means:

- the receiving endpoint will receive the data under `req.data` and is
not able to read it from the search params
- this means existing endpoints won't support this functionality unless
they also attempt to read from req.data.
- for the purpose of this PR, the findByID endpoint was modified to
support this behavior. This functionality is documented as it can be
useful for user-defined endpoints as well.

Passing data as json has the following benefits:

- it's more performant - no need to serialize and deserialize data to
search params via `qs-esm`. This is especially important here, as we are
passing large amounts of json data
- the current implementation was serializing the data incorrectly,
leading to incorrect data within nested lexical nodes

**Note for people passing their own live preview `requestHandler`:**
instead of sending a GET request to populate documents, you will now
need to send a POST request to the findByID endpoint and pass additional
headers. Additionally, you will need to send through the arguments as
JSON instead of search params and include `data` as an argument. Here is
the updated defaultRequestHandler for reference:

```ts
const defaultRequestHandler: CollectionPopulationRequestHandler = ({
  apiPath,
  data,
  endpoint,
  serverURL,
}) => {
  const url = `${serverURL}${apiPath}/${endpoint}`

  return fetch(url, {
    body: JSON.stringify(data),
    credentials: 'include',
    headers: {
      'Content-Type': 'application/json',
      'X-Payload-HTTP-Method-Override': 'GET',
    },
    method: 'POST',
  })
}
```

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211124793355068
  - https://app.asana.com/0/0/1211124793355066
2025-09-05 14:40:52 -04:00
2024-08-16 15:22:56 -04:00
2024-08-13 12:54:33 -04:00
2025-09-04 16:11:17 +01:00
2025-09-04 16:11:17 +01:00
2025-08-28 09:47:54 -04:00
2025-03-05 19:14:35 +00:00
2025-07-07 20:00:02 +00:00

Payload headless CMS Admin panel built with React

GitHub Workflow Status   Discord   npm   npm   npm   Payload Twitter


Explore the Docs · Community Help · Roadmap · View G2 Reviews


Important

🎉 We've released 3.0! Star this repo or keep an eye on it to follow along.

Payload is the first-ever Next.js native CMS that can install directly in your existing /app folder. It's the start of a new era for headless CMS.

Benefits over a regular CMS

  • Deploy anywhere, including serverless on Vercel for free
  • Combine your front+backend in the same /app folder if you want
  • Don't sign up for yet another SaaS - Payload is open source
  • Query your database in React Server Components
  • Both admin and backend are 100% extensible
  • No vendor lock-in
  • Never touch ancient WP code again
  • Build faster, never hit a roadblock

Quickstart

Before beginning to work with Payload, make sure you have all of the required software.

pnpx create-payload-app@latest

If you're new to Payload, you should start with the website template (pnpx create-payload-app@latest -t website). It shows how to do everything - including custom Rich Text blocks, on-demand revalidation, live preview, and more. It comes with a frontend built with Tailwind all in one /app folder.

One-click templates

Jumpstart your next project by starting with a pre-made template. These are production-ready, end-to-end solutions designed to get you to market as fast as possible.

🌐 Website

Build any kind of website, blog, or portfolio from small to enterprise. Comes with a fully functional front-end built with RSCs and Tailwind.

We're constantly adding more templates to our Templates Directory. If you maintain your own template, consider adding the payload-template topic to your GitHub repository for others to find.

Features

Request Feature

🗒️ Documentation

Check out the Payload website to find in-depth documentation for everything that Payload offers.

Migrating from v2 to v3? Check out the 3.0 Migration Guide on how to do it.

🙋 Contributing

If you want to add contributions to this repository, please follow the instructions in contributing.md.

📚 Examples

The Examples Directory is a great resource for learning how to setup Payload in a variety of different ways, but you can also find great examples in our blog and throughout our social media.

If you'd like to run the examples, you can use create-payload-app to create a project from one:

npx create-payload-app --example example_name

You can see more examples at:

🔌 Plugins

Payload is highly extensible and allows you to install or distribute plugins that add or remove functionality. There are both officially-supported and community-supported plugins available. If you maintain your own plugin, consider adding the payload-plugin topic to your GitHub repository for others to find.

🚨 Need help?

There are lots of good conversations and resources in our Github Discussions board and our Discord Server. If you're struggling with something, chances are, someone's already solved what you're up against. 👇

Like what we're doing? Give us a star

payload-github-star

👏 Thanks to all our contributors

Description
No description provided
Readme 110 MiB
Languages
TypeScript 95.9%
SCSS 2.7%
JavaScript 1.1%
CSS 0.2%
Dockerfile 0.1%