I just recently discovered Payload and fell in love with it immediately.
I'm now trying to set up a live preview with the NextJS Frontend which is deployed on vercel. The goal would be to have 2 windows open, edit the content in payload on the left side, see the changes on the preview website in real time (small delays are fine).
My Idea was to set up a separate deploy with an auth wall on a subdomain (preview.xyz.com) that get's the data in draft mode after the user has logged in with his payload credentials. Using the autosave feature, the content on the preview page should be updated every 2 seconds.
One unsolved question is how to actually refresh the page content: I could let the page query the content every 2 seconds, or maybe set up a websocket connection to the payload express app? Since I haven't done anything similar before, I'd love to hear if anyone thinks this will work, or if there are better/simpler ways to implement this.
Hey
@900030932617162793— I do think that a simple websocket or messaging service would be the best way to do this
and this is on our radar to build into Payload for sure, natively, although as of right now, you could do this on your own using SocketIO or similar
Sandro you are doing the lords work, if you figure it out lmk. Would love to implement a live preview, I wish there was documentation on implementation of just the preview functionality with next similar to what Prismic, DatoCMS, etc... Trying to hack something together to show team proof of concept
Same here, would love this! As of right now I just refetch the draft data each 2seconds on a preview route.
Using SocketIO seems interesting for sure.
I'll post in here when I've figured sth out 😉
@839912789560000522
I actually managed to get it working. Check my post in the Showcase Channel or hmu 🙂
Very nice! I would love to try and implement this in my project. It's SvelteKit tho, but the functionality stays the same I guess.
Yup, you just have to implement a functionality in the frontend to 1. Authenticate a user (send a login request to payload) and 2. if logged in, listen to update calls from the server. This is a bit tricky if you're using Nextjs along with SSG etc. but I don't know about sveltekit
Here's what you have to do serverside
// Instead of just calling app.listen
// app.listen(process.env.PAYLOAD_PUBLIC_INTERNAL_SERVER_URL.split(':')[2]);
// create an http server (or https if you aren't using a reverse proxy)
const server = http.createServer(app)
server.listen(process.env.PAYLOAD_PUBLIC_INTERNAL_SERVER_URL.split(':')[2], () => {
console.log(`HTTP Server running on port ${process.env.PAYLOAD_PUBLIC_INTERNAL_SERVER_URL.split(':')[2]}`);
});
// Socket.io
// Now pass the server instance to socket io, to create a websocket server on the same port
const io = require('socket.io')(server, {
cors: {
origin: process.env.WHITELIST_ORIGINS.split(','),
}
});
io.on('connection', (socket) => {
console.log('New client connected');
socket.on('disconnect', () => {
// console.log('Client disconnected')
});
})
// You can't call io.emit directly from the payload hooks, it throws errors.
// As a workaround, you can set up a simple endpoint that emits the update
app.post('/updatePreview', (req, res) => {
console.log('update preview called')
// Check if authorization header contains PAYLOAD_SECRET
if (req.headers.authorization !== process.env.PAYLOAD_PUBLIC_SERVER_SECRET) {
res.sendStatus(401)
return
}
io.emit('update')
})
Now, in the payload hooks, add an afterChange Hook:
if (args.doc._status !== 'published') {
try {
axios({
method: 'post',
url: `${process.env.PAYLOAD_PUBLIC_INTERNAL_SERVER_URL}/updatePreview`,
headers: {
'Authorization': `${process.env.PAYLOAD_PUBLIC_SERVER_SECRET}`
}
})
} catch (e) {
console.error(e)
}
return console.log('Not published, skipping revalidation')
This will just call the api endpoint you've just set up and emit the update message
if you write code blocks and want them to be syntax highlighted for readability, you can append the language after the 3 back ticks. For typescript it would be 3 back ticks followed by the word
typescript
Oh damn thanks
@281120856527077378! This is my first time really using discord haha
No prob! Was just letting you know 😃
finally had the chance to test this out. Works like a charm!
Gosh I'm a real grave-digger here, but if anyones interested, my solution (developed accidentally in parallel with this one haha) is perhaps a bit more involved, but the authentication part is quite interesting so I figured I should probably post it. It does require the two apps (frontend and payload) to be one the same-ish URL (ie. www.example.com and cms.example.com) but works pretty well I would say!
export const io = new Server(server, {
cors: {
origin: WEB_URL,
credentials: true,
},
allowRequest: (req, callback) => {
const cookies = Object.fromEntries(
req.headers.cookie
?.split("; ")
.map((cookie) => cookie.split("="))
.filter((tuple) => tuple.length === 2) ?? []
);
const payloadToken = cookies["payload-token"];
if (typeof payloadToken !== "string") {
callback("Not signed in", false);
return;
}
jwt.verify(payloadToken, payload.secret, (err) => {
if (err) {
callback("Invalid token", false);
return;
}
callback(null, true);
});
},
});
Star
Discord
online
Get dedicated engineering support directly from the Payload team.