Configure your own Document Preview

Added in: release-2023-07

Register Document Preview Functions

First, you need to register Document Preview Functions in the server. You can register as many as you want.

liServer.registerInitializedHook(async () => {
  const documentApi = liServer.features.api('li-documents').document

// return html ...
  handle: 'myHtmlPreviewFunction',
  async getPreview ({projectConfig, documentId}) {
    const doc = await documentApi.getLatestDocument(documentId)
    return {
      html: `<div><h1>Title: ${doc.title}</h1><p>This is a custom preview</p></div>`

// ... or an iframe
    handle: 'myIframePreviewFunction',
    async getPreview ({projectConfig, documentId}) {
      return {
        iframe: {
          src: `${documentId}`,
          sandbox: 'allow-same-origin' // make sure this is set if you want to preserve scroll position

Configure global Document Previews

With the functions registered, you now define the Document Previews in the project config on editorSettings.

// editorSettings
  documentPreviews: [
      handle: 'htmlPreview',
      previewFunction: 'myHtmlPreviewFunction',
      icon: 'book-edit',
      label: 'Preview'
      handle: 'iframePreview',
      previewFunction: 'myIframePreviewFunction',
      icon: 'pencil',
      label: {
        de: 'Meine Seite',
        en: 'My Site'

Define Document Previews per Content-Type

For every ContentType, you can define the usable DocumentPreviews. This is done on the ContentType configuration:

  handle: 'myContentType',
  // ...
  // for documentPreviews, define the handles of editorSettings.documentPreviews
  documentPreviews: ['iframePreview', 'htmlPreview']

Preserve scroll position on reload

If you return {html}, you don’t have to do anything. Livingdocs will preserve the scroll position when the user reloads the preview. If you return {iframe: {src: ''}}, you need to communicate with Livingdocs using a postMessage interface. It works like this:

Within your iframe, add this script block:

  // set this to the origin of the Livingdocs Editor
  const parentOrigin = 'https://localhost:9000'

  window.addEventListener('scroll', () => {
    window.requestAnimationFrame(() => {
      // when the user scrolls within your iframe
      // send the scroll position to Livingdocs to store it for later
        action: 'saveScrollPosition',
        position: {
          top: document.querySelector('body').scrollTop
      }, parentOrigin)

  window.addEventListener("message", (event) => {
    if (event.origin !== parentOrigin) return
    if ( === 'setScrollPosition') {
      // contains the data you sent with
      // the saveScrollPosition message
      document.querySelector('body').scrollTop =