-
Notifications
You must be signed in to change notification settings - Fork 73
Description
Hey Guys,
i recently integrated the directus visual editor in my Next.js App router project.
I have also done the same with other headless CMS, namely Contentful and Storyblok.
Generally, i understand that the goal is to make Guides/Tutorials on the directus docs framework agnostic. However, there is two perspectives from which this task could be implemented framework agnostic. The one being the way it is currently documented, the other being from Next.js perspective, where all visual editors and similar tools are implemented in the same way as well, ever since Next.js moved to App router.
The introduction of React Server Components (RSC) meant a shift in approach for lots of stuff and it is obvious that all services and tools can't change their docs over night to accomodate for this. The original approach of implementing such services into Next.js (and all other frameworks) is now contradicting with the recommended usage of RSC, where all components are meant to be server-side by default.
Therefore, i would advocate for changing the docs regarding this topic to the modern RSC-compliant way.
I am going to give a quick example of what i mean:
import CmsProvider from '@/_lib/cms/provider'
/*
* create a Wrapper component for all additional client-side services and use them in the root layout.
* in this example i created the <CmsProvider> for implementing the directus visual editor
*/
export default async function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="de" suppressHydrationWarning>
<body suppressHydrationWarning>
<CmsProvider>{children}</CmsProvider>
</body>
</html>
)
}
What is the benefit of this?
Well, the concept of RSC implies that client components cannot not contain server components, meaning that any usage of a client component at the top of the component hierarchy, will automatically negate the whole purpose of RSCs.
The only exception to this rule is the root layout, where client-side wrapper components or providers can exist without transforming every child component into a client-side one (even though, this is technically a violation of the rule).
The provider in this case would look somewhere similar to this:
'use client'
import { useEffect } from 'react'
import { usePathname } from 'next/navigation'
import { suspendVisualEditor, initializeVisualEditor } from '@/_lib/cms/visualEditor'
export default function CmsProvider({ children }: { children: React.ReactNode }) {
const pathname = usePathname()
useEffect(() => {
// check if embedded in an iframe
if (typeof window === 'undefined' || window.top === window.self) return
// persist directus embedded status in session storage and toggle draft mode + visual editor accordingly
const isDirectusIframe = document.referrer.includes(process.env.NEXT_PUBLIC_CMS_BASE_URL)
const wasInDirectus = typeof window !== 'undefined' && sessionStorage.getItem('directus-embedded') === '1'
const acceptVisualEditing = isDirectusIframe || wasInDirectus
const hasDirectusCookie = document.cookie.includes('preview_client')
if (acceptVisualEditing && process.env.NEXT_PUBLIC_ENV === 'staging') {
document.body.classList.add('draft-mode')
sessionStorage.setItem('directus-embedded', '1')
// init visual editor if cookie is already present, enable draft mode (sets cookie) otherwise
if (hasDirectusCookie) {
initializeVisualEditor())
} else {
fetch(`/api/draft?slug=${pathname}`)
.then((response) => response.json())
.then((data) => {
console.info('draft mode enabled')
if (!data.redirect) return
initializeVisualEditor()
})
.catch((error) => console.error(error))
}
} else {
document.body.classList.remove('draft-mode')
sessionStorage.removeItem('directus-embedded')
// disable draft mode + suspend visual editor
fetch('/api/draft?disable=true')
.then(() => console.info('draft mode disabled'))
.catch((error) => console.error(error))
suspendVisualEditor()
}
}, [pathname])
return <>{children}</>
}
I would be willing to create a pull request for this, but i wanted to ask beforehand, if this is generally in the spirit of the directus docs.
Cheers!