Hooks API
The Hooks API provides a WordPress-style system for actions and filters.
Actions
Actions allow you to execute code at specific points in the CMS lifecycle.
sdk.hooks.addAction(name, callback, priority)
Register an action hook.
sdk.hooks.addAction('post.viewed', async (post) => {
console.log('Post viewed:', post.title);
// Track view, send notification, etc.
}, 10);Parameters:
| Parameter | Type | Description |
|---|---|---|
name | string | Hook name |
callback | function | Function to execute |
priority | number | Execution order (lower = earlier, default: 10) |
sdk.hooks.removeAction(name, callback)
Remove a previously registered action.
const myCallback = (post) => console.log(post.title);
sdk.hooks.addAction('post.viewed', myCallback);
// Later...
sdk.hooks.removeAction('post.viewed', myCallback);sdk.hooks.doAction(name, ...args)
Trigger an action (primarily for themes).
await sdk.hooks.doAction('my_custom_action', data);Filters
Filters allow you to modify data as it passes through the system.
sdk.hooks.addFilter(name, callback, priority)
Register a filter hook.
// Modify post title
sdk.hooks.addFilter('post.title', (title) => {
return title.toUpperCase();
}, 10);
// Modify post content
sdk.hooks.addFilter('post.content', (content) => {
// Add something to content
return content + '<p>Thanks for reading!</p>';
}, 20);Parameters:
| Parameter | Type | Description |
|---|---|---|
name | string | Filter name |
callback | function | Function that receives and returns data |
priority | number | Execution order (lower = earlier, default: 10) |
sdk.hooks.removeFilter(name, callback)
Remove a previously registered filter.
const myFilter = (title) => title.toUpperCase();
sdk.hooks.addFilter('post.title', myFilter);
// Later...
sdk.hooks.removeFilter('post.title', myFilter);sdk.hooks.applyFilter(name, value, ...args)
Apply all registered filters to a value.
const filteredTitle = await sdk.hooks.applyFilter('post.title', post.title);Available Hooks
Actions
| Hook Name | Arguments | Description |
|---|---|---|
init | - | CMS has initialized |
post.viewed | post | A post was viewed |
post.saved | post | A post was saved |
post.deleted | postId | A post was deleted |
category.saved | category | A category was saved |
author.saved | author | An author was saved |
plugin.activated | pluginId | A plugin was activated |
plugin.deactivated | pluginId | A plugin was deactivated |
theme.switched | themeId | Theme was changed |
Filters
| Filter Name | Input | Description |
|---|---|---|
post.title | title | Filter post title |
post.content | content | Filter post HTML content |
post.excerpt | excerpt | Filter post excerpt |
post.data | post | Filter entire post object |
page.title | title | Filter page/document title |
page.meta | meta | Filter page meta tags |
Theme Hooks (for templates)
{# In your theme templates #}
{{{ hooks.do('theme_head') }}}
{{{ hooks.do('theme_body_start') }}}
{{{ hooks.do('theme_before_content') }}}
{{{ hooks.do('theme_after_content') }}}
{{{ hooks.do('theme_body_end') }}}
{{{ hooks.do('theme_after_post') }}}
{{{ hooks.do('theme_home_before') }}}
{{{ hooks.do('theme_home_after_hero') }}}Examples
Track Post Views
module.exports = async function(sdk) {
sdk.hooks.addAction('post.viewed', async (post) => {
// Get current view count
const stats = await sdk.storage.get('viewStats') || {};
stats[post.id] = (stats[post.id] || 0) + 1;
await sdk.storage.set('viewStats', stats);
});
return { activate: async () => {}, deactivate: async () => {} };
};Auto-Link Keywords
module.exports = async function(sdk) {
const keywords = {
'FeedGen CMS': 'https://feedgen.net',
'documentation': '/docs'
};
sdk.hooks.addFilter('post.content', (content) => {
Object.entries(keywords).forEach(([word, url]) => {
const regex = new RegExp(`\\b${word}\\b`, 'gi');
content = content.replace(regex, `<a href="${url}">${word}</a>`);
});
return content;
}, 20);
return { activate: async () => {}, deactivate: async () => {} };
};Add Custom Meta Tags
module.exports = async function(sdk) {
sdk.hooks.addFilter('page.meta', (meta) => {
meta.push('<meta name="author" content="My Site">');
meta.push('<meta property="og:site_name" content="My Site">');
return meta;
});
return { activate: async () => {}, deactivate: async () => {} };
};Inject Content Into Theme
module.exports = async function(sdk) {
// Add analytics to head
sdk.hooks.addAction('theme_head', async () => {
return `<script>/* Analytics code */</script>`;
});
// Add banner after content
sdk.hooks.addAction('theme_after_content', async () => {
return `<div class="promo-banner">Check out our deals!</div>`;
});
return { activate: async () => {}, deactivate: async () => {} };
};Best Practices
- Use appropriate priorities - Lower numbers run first (10 is default)
- Always return filtered values - Filters must return the modified data
- Handle errors gracefully - Wrap async code in try/catch
- Keep callbacks efficient - Hooks run on every request
- Remove hooks on deactivate - Clean up when plugin is disabled
- Namespace your hooks - Prefix custom hooks with plugin name