@flotiq/nextjs-live-preview
v0.7.1
Published
Support for live preview in Next.JS framework in flotiq-api-sdk
Readme
@flotiq/nextjs-live-preview
A comprehensive Next.js integration for Flotiq CMS that provides real-time live preview functionality through WebSocket connections. This package allows content editors to see their changes in real-time as they edit content in the Flotiq CMS.
Features
- 🔄 Real-time content updates via WebSocket connections
- 👥 Multi-user editing indicators showing who's currently editing each field
- 🎯 Auto-scroll to active fields when editors focus on content
- 🔗 Relation field hydration supporting 2 levels of hydration
- 🏷️ Smart relation cache invalidation with Next.js cache tags
- 📱 Flotiq iframe support for seamless CMS integration
- ⚡ Throttled updates to prevent excessive re-renders
- 🎨 Customizable UI components for editor indicators
Installation
npm install @flotiq/nextjs-live-previewRequirements
- React: 19.0.0 or higher
- Next.js: 15.0.0 or higher
- Flotiq API SDK: 0.6.0 or higher
How It Works
- WebSocket Connection: The middleware establishes WebSocket connections to Flotiq's real-time servers
- Content Interception: API requests for content objects are intercepted and enhanced with live data
- Real-time Updates: Changes from the CMS are pushed via WebSocket and trigger React re-renders
- Editor Awareness: Multiple editors' activities are tracked and displayed with colored indicators
Quick Start
1. Configure the Middleware
Add the live preview middleware to your Flotiq API configuration:
import { Flotiq } from "@flotiq/flotiq-api-sdk";
import { createNextLivePreviewMiddleware } from "@flotiq/nextjs-live-preview";
const api = new Flotiq({
middleware: [createNextLivePreviewMiddleware()],
});Function createNextLivePreviewMiddleware supports provided options:
- flotiqApiKey - optional. It uses
FLOTIQ_API_KEYenv by default. It can be used to override Flotiq API key - connectionMode - optional. Possible options:
wsorhttp. It useswsmode by default. It is connection mode used by server. More about connetion modes
2. Create Live Preview API Route
Create the API route that handles live preview mode toggling in app/api/flotiq/live-preview/route.ts file.
import { NextRequest } from "next/server";
import { redirect } from "next/navigation";
import { livePreview } from "@flotiq/nextjs-live-preview/server";
export async function GET(req: NextRequest) {
const redirectPath = req.nextUrl.searchParams.get("redirect") || "/";
/**
* Validate the request.
*
* To validate the request you can use the `Client Authoristion Key` passed to the live preview URL from **Live Preview** plugin as `editor_key` URL param.
* Make sure to define it in your application's environment variables and passed the same `Client Authoristion Key` to the plugin settings.
* Without validating this key, everybody could see your latest data.
*
* Example validation:
*
* const clientAuthKey = req.nextUrl.searchParams.get('editor_key');
* if (clientAuthKey !== process.env.FLOTIQ_CLIENT_AUTH_KEY) {
* return new Response('Unauthorized', { status: 401 });
* }
*/
const livePreviewState = await livePreview();
const newLivePreview = req.nextUrl.searchParams.has("live-preview")
? req.nextUrl.searchParams.get("live-preview") === "true"
: !livePreviewState.isEnabled;
if (newLivePreview) {
livePreviewState.enable(req);
} else {
livePreviewState.disable();
}
redirect(redirectPath);
}3. Add Live Preview Status Component
This component is need for router to refresh to see latest data. It is adding the banner with live preview informations.
If you don't want to use predefined banner, you can use useLivePreviewStatus hook and create your own component (more info).
import { livePreview } from "@flotiq/nextjs-live-preview/server";
import { LivePreviewStatus } from "@flotiq/nextjs-live-preview/client";
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const { isEnabled } = await livePreview();
return (
<html>
<body>
<main>
{isEnabled && (
<LivePreviewStatus editorKey={process.env.FLOTIQ_EDITOR_KEY} />
)}
{children}
</main>
</body>
</html>
);
}4. Cache Revalidation Route (Required for HTTP Mode)
When using HTTP mode, you need to create an additional API route for cache revalidation. Without this route, relation fields will retain their cache from the first visit and data won't update properly.
Create a file at app/api/flotiq/live-preview/revalidate/route.ts:
import { NextRequest } from 'next/server';
import { livePreview } from '@flotiq/nextjs-live-preview/server';
export async function POST(req: NextRequest) {
const contentType = req.nextUrl.searchParams.get('content_type') || '';
const id = req.nextUrl.searchParams.get('id') || '';
const { clearCache } = await livePreview();
clearCache(contentType, id);
return new Response('Live preview cache cleared', { status: 200 });
}This route calls the clearCache method from the live preview package. The method checks if live preview is enabled, and if not, nothing happens.
After creating this route, pass its URL to the LivePreviewStatus component or useLivePreviewStatus hook:
<LivePreviewStatus
editorKey={process.env.FLOTIQ_EDITOR_KEY}
revalidateUrl="/api/flotiq/live-preview/revalidate"
/>When relation fields change, the frontend will send a fetch request to this route if the URL is provided. If not provided, nothing happens.
Note: The WebSocket (ws) mode clears the cache on the server side automatically, so this route is not required for ws mode. However, you can still use it if you want to perform additional actions when relations change.
You can also add custom parameters to the URL if needed, for example:
revalidateUrl="/api/flotiq/live-preview/revalidate?my_param=test"Server Connection Modes
WebSocket Mode (Recommended)
It uses persistent connections to websocket.
How it works:
- The middleware and frontend component establish WebSocket connections to Flotiq's real-time servers.
- When editing content object, Flotiq Live Preview plugin sends information to the application.
- API requests for content objects are intercepted and enhanced with live data in middleware.
- Frontend component is refreshing the router so that the latest data can be seen.
HTTP Mode
It uses persistent connections to websocket only in frontend component. But the middleware is not. This method allows using live preview in deployed application where the connection cannot be persistent for the server, eg. Vercel.
How it works:
- The frontend component establish WebSocket connections to Flotiq's real-time servers.
- When editing content object, Flotiq Live Preview plugin sends information to the frontend component.
- Middleware intercepts API requests for content objects and fetches newset data from special enpoint prepared in webscoket for getting latest data.
- Frontend component is refreshing the router so that the latest data can be seen.
Advanced Usage
Live Preview Boxes for Field Editing
Wrap your content fields with LivePreviewBox to show visual editing indicators and enable field-specific interactions:
import { LivePreviewBox } from "@flotiq/nextjs-live-preview/server";
export default async function BlogPost({
params,
}: {
params: { slug: string };
}) {
const post = await flotiqApi.ContentBlogpostAPI.get(params.slug, {
hydrate: 1,
});
return (
<article>
<LivePreviewBox objectId={post.id} ctdName="blogpost" fieldName="title">
<h1>{post.title}</h1>
</LivePreviewBox>
</article>
);
}Custom Live Preview Hook
If you doesn't want to use LivePreviewStatus component, you can use useLivePreviewStatus hook.
This hook is doing refreshes to the router on data update.
Exmaple code with custom banner:
"use client";
import { useLivePreviewStatus } from "@flotiq/nextjs-live-preview/client";
export function CustomLivePreviewIndicator() {
const { isConnected, updatedTime, contentType, objectId } =
useLivePreviewStatus();
if (!isConnected) return null;
return (
<div className="live-preview-custom">
<span>
Editing {contentType}#{objectId}
</span>
{updatedTime && <span>Last update: {updatedTime}</span>}
</div>
);
}Configuration Options
Middleware Options
interface NextLivePreviewMiddlewareOptions {
/**
* Flotiq API key for authorization
* @default process.env.FLOTIQ_API_KEY
*/
flotiqApiKey?: string;
/**
* Connection mode for live preview data
* - "ws": Active WebSocket connection (default, recommended)
* - "http": HTTP polling from WebSocket server
*/
connectionMode?: "ws" | "http";
}LivePreviewBox Props
interface LivePreviewBoxProps {
/** Field content to wrap */
children: React.ReactNode;
/** Field name in the content object */
fieldName: string;
/** Content object ID */
objectId: string;
/** Content Type Definition name */
ctdName: string;
/** Scroll offset for auto-focus (default: 0) */
scrollTopOffset?: number;
/** Use light color theme for editor indicators (default: false) */
lightColors?: boolean;
/** Additional CSS class name */
className?: string;
}LivePreviewStatus Props
interface LivePreviewStatusProps {
/** Client Authorization key */
editorKey: string;
/** URL to cache revalidation route (required for HTTP mode) */
revalidateUrl?: string;
/** Additional CSS class name */
className?: string;
}Troubleshooting
- Ensure cookies are enabled in your browser
- Check that the API route is correctly implemented
- Verify the
Client Authorisation Keymatches in both Flotiq and application - Verify if deployed application is allowing persistent connections. Otherwise change the connection mode to
httpmethod
