Skip to content

Instantly share code, notes, and snippets.

@notflip
Last active January 12, 2025 11:20
Show Gist options
  • Save notflip/5103aaf2b1e79a5b1c73549c193736cd to your computer and use it in GitHub Desktop.
Save notflip/5103aaf2b1e79a5b1c73549c193736cd to your computer and use it in GitHub Desktop.
Payload CMS Estimated Reading Time Hook
import {CollectionBeforeValidateHook} from 'payload';
import {
consolidateHTMLConverters,
convertLexicalToHTML,
defaultEditorConfig,
defaultEditorFeatures,
HTMLConverterFeature,
sanitizeServerEditorConfig
} from "@payloadcms/richtext-lexical";
import {calculateReadingTime} from "@/lib/utils";
export const generateReadingTime: CollectionBeforeValidateHook = async ({data, operation, req}) => {
if (operation === 'create' || operation === 'update') {
const editorConfig = defaultEditorConfig
editorConfig.features = [...defaultEditorFeatures, HTMLConverterFeature({})]
const sanitizedEditorConfig = await sanitizeServerEditorConfig(editorConfig, req.payload.config)
const html = await convertLexicalToHTML({
converters: consolidateHTMLConverters({editorConfig: sanitizedEditorConfig}),
data: data!.content,
req,
})
return {
...data,
readingTime: calculateReadingTime(html),
};
}
};
import {CollectionConfig} from "payload";
import {generateReadingTime} from "@/hooks/generateReadingTime";
export const Posts: CollectionConfig = {
slug: 'posts',
versions: {
maxPerDoc: 50,
drafts: {
autosave: {
interval: 150,
},
},
},
fields: [
{
name: "readingTime",
type: "number",
admin: {
hidden: true,
}
}
],
hooks: {
beforeValidate: [generateReadingTime]
}
}
export function calculateReadingTime(text: string) {
if (!text) return 0;
const plainText = text.replace(/<[^>]+>/g, '');
const wordCount = plainText.trim().split(/\s+/).length;
const wordsPerMinute = 200;
return Math.ceil(wordCount / wordsPerMinute);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment