Explore everything FotoGrids can do
Style every gallery to your brand
Drag, drop, publish. No code
Rank higher. Load lighter
Your photos stay your photos
Track. Engage. Grow
Native block for the WordPress editor
Dedicated widget with full control
Custom module for any Divi layout
Fully customizable native element
Anywhere WordPress renders
Read, write, and automate galleries
Customize behavior with WP hooks
Bring your own styles and scripts
Hooks, REST API, and full control
Showcase your work beautifully
Collaborate, deliver, and scale client work
Beautiful galleries without writing code
Turn visitors into paying customers
Product galleries built to sell
Creative portfolios for every field
Curated video playlists and embeds
Conferences, weddings and celebrations
E-Commerce-ready product showcases
Property photos that close deals
Client proofing with secure galleries
FotoGrids ships 140+ distinct hook names and 20+ JavaScript events — all of them in Free. One naming rule, three scopes on every render hook, and a public render API that Pro itself is built on top of.
fotogrids/render/final_html fotogrids/render/collection_items fotogrids/render/anchor_attrs fotogrids/render/wrapper_css_classes fotogrids/render/wrapper_data_attrs fotogrids/render/css_variables fotogrids/render/active_modules fotogrids/render/layout/style_vars fotogrids/render/layout/wrapper_attrs fotogrids/render/register_modules fotogrids/render/should_inline_assets fotogrids/render/breakpoint_config fotogrids/view/gallery_html fotogrids/view/before_gallery fotogrids/view/head_meta fotogrids/view/og/image fotogrids/view/body_classes fotogrids/view/footer_credit fotogrids/lightbox/slides fotogrids/actions/item/added fotogrids/actions/item/meta/updated fotogrids/actions/gallery/reordered fotogrids/data/exif/extract fotogrids/breadcrumb/render_html fotogrids/settings/defaults/gallery fotogrids/settings/edit_gate fotogrids/catalog/json_files fotogrids/catalog/field_state fotogrids/cache/should_cache fotogrids/cache/bucket fotogrids/cache/flushed_for_gallery fotogrids/features/pro/can_use fotogrids/features/access_state fotogrids/permissions/check fotogrids/templates/can_apply fotogrids/seo/resolved Hook names the fan-out Hook names
fotogrids/render/final_html // every collection
fotogrids/render/final_html/{gallery|album} // one type
fotogrids/render/final_html/{gallery|album}/{id} // one collection
The bases live under fotogrids/render/:
the actions before_render and after_render, and the filters render_settings, collection_items, wrapper_css_classes, wrapper_data_attrs, css_variables, active_modules, final_html and anchor_attrs.
Each one fans out into three concrete WordPress hooks, fired in that order. The callback signature is identical at all three, so moving a change from every collection on the site down to one Gallery means editing the hook string and nothing else.
PHP functions.php PHP
add_filter( 'fotogrids/render/final_html', function ( $html, $render ) {
return $html . '© ' . esc_html( get_bloginfo( 'name' ) ) . '
';
}, 10, 2 );
PHP functions.php PHP
add_filter( 'fotogrids/render/final_html/gallery/42', function ( $html, $render ) {
return str_replace( 'fg-grid', 'fg-grid my-custom-class', $html );
}, 10, 2 );
$html is the complete rendered collection markup. $render is a FotoGrids\Render\Api\Render_Context with public properties ->meta, ->layout, ->behavior, ->settings, ->items, ->warnings and ->via_album_id. Return a non-string and it is discarded — the original HTML is kept. PHP
anchor_attrs PHP
add_filter( 'fotogrids/render/anchor_attrs', function ( array $attrs, $render ) {
$attrs['data-elementor-open-lightbox'] = 'no';
$attrs['data-no-lazy'] = '1';
return $attrs;
}, 10, 2 );
$attrs (array<string,string> — the attribute map on the <a> wrapping each item: href, target, rel, data-fg-item-id) and $render.
Fired from five decorators — external link, Lightbox, direct link, Album to View Page, Album to Gallery AJAX — and cast to array at every call site. PHP
collection_items PHP
add_filter( 'fotogrids/render/collection_items', function ( array $items, $render ) {
return array_slice( $items, 0, 12 );
}, 10, 2 );
$collection_items (array<int, FotoGrids\Render\Api\Item_View> the item list after every decorator has run, before layout selection) and $render. The array you return is written straight back into the render context, so it changes what the layout renders, what the Lightbox advertises and the pagination totals. Return an array. PHP
lightbox/slides
PHP
add_filter( 'fotogrids/lightbox/slides', function ( array $slides, array $ids, array $settings ) {
foreach ( $slides as $i => $slide ) {
$slides[ $i ]['price'] = get_post_meta( $ids[ $i ], '_price', true );
}
return $slides;
}, 10, 3 );
$slides (resolved slide payloads; per-slide keys include item_type, thumb_url, full_url, video_src, embed_provider, embed_id, embed_settings, and exif when EXIF display is on), $ids (attachment IDs in slide order) and $settings. PHP
cache
PHP
add_filter( 'fotogrids/cache/should_cache', function ( $should, array $settings, int $gallery_id ) {
return is_user_logged_in() ? false : $should;
}, 10, 3 );
add_filter( 'fotogrids/cache/bucket', function ( $bucket, array $settings, int $gallery_id ) {
return is_user_logged_in() ? 'member' : $bucket;
}, 10, 3 );
should_cache takes $should_cache (bool), $settings and $gallery_id. bucket takes $bucket (string, default 'default') plus the same two — and is mixed into the cache key, so per-role or per-user variants get their own entries. Returning true from should_cache cannot re-enable caching in a preview, AJAX or REST context: those bail out before the filter is reached. PHP
register_modules PHP
add_action( 'fotogrids/render/register_modules', function () {
\FotoGrids\Render\Internal\Module_Registry::register(
'layouts',
\My\Plugin\Layout_Spiral::class // implements FotoGrids\Render\Api\Layout
);
}, 20 );
Module_Registry::register( string $slot, string $class ).
It is the same door Pro comes through. Sibling: fotogrids/render/register_hover_effects, also no arguments. PHP view/before_gallery PHP
add_action( 'fotogrids/view/before_gallery', function ( \WP_Post $post ) {
echo '' . esc_html( get_the_excerpt( $post ) ) . '
';
} );
\WP_Post: fotogrids/view/head, before_shell, header, before_gallery, after_gallery, footer, after_shell. The filter fotogrids/view/gallery_html takes $html and $post. PHP save/item/metadata PHP
add_filter( 'fotogrids/save/item/metadata', function ( array $results, int $item_id,
\WP_REST_Request $request ) {
$moods = (array) $request->get_param( 'moods' );
if ( $moods ) {
update_post_meta( $item_id, '_fg_moods', $moods );
$results['moods'] = $moods;
}
return $results;
}, 10, 3 );
metadata key of the REST response. wp.hooks actions or filters of its own, and dispatches no jQuery events. addEventListener is the whole API. fotogrids:ready
fotogrids:gallery_inserted
galleryElement, galleryId and kind. fotogrids:gallery_initialized
instance. fotogrids:share
itemId and network. fotogrids:gallery_unlocked
galleryId. fotogrids:album_swapped
albumEl and galleryId. fotogrids:album_restored
albumEl. fotogrids/filters/ready
document, so a single listener covers every Gallery on the page. fotogrids:lightbox:open
galleryEl. fotogrids:lightbox:close
galleryEl. fotogrids:lightbox:navigate
galleryEl. These fire on the Gallery element and bubble, so you can listen on document or scope to one render. e.detail.galleryEl tells you which Gallery they came from.
fotogrids:page_changed
fotogrids:items_inserted
fotogrids:filters_changed
items_inserted rather than page_changed when your work has to run against the newly added items. fotogrids:collection_saved
fotogrids:gallery_save_error
fotogrids:setting_changed
fotogrids:tool-component-registered
fotogrids:admin:modal:opened
fotogrids:admin:modal:closed
fotogrids:admin:modal:confirmed
fotogrids:admin:modal:tab-changed
fotogrids:admin:modal: prefix. Pro has its own autoloader and finds Free through the FOTOGRIDS_VERSION constant. Nothing else is shared at load time.
The fotogrids/features/pro/ filters are where licence answers come from. fotogrids/features/pro/can_use takes $can_use — always false in Free — and $feature_id.
fotogrids/render/register_modules is how Pro’s own layouts and decorators enter the render pipeline. fotogrids/catalog/json_files puts its settings fields into the admin UI.
FotoGrids\Render\Api\Render_Context, Module_Assets, Asset_Decl and the Layout and Decorator interfaces — the same classes you get. A handful of hooks are integration seams — the ones a plugin attaches to when it wants to sit inside FotoGrids rather than beside it. Pro comes through these; so can you.
fotogrids/features/pro/is_active fotogrids/features/pro/can_use fotogrids/features/pro/on_plan fotogrids/features/pro/enabled fotogrids/admin/page_hooks fotogrids/catalog/json_files fotogrids/render/register_modules Registers layouts, decorators and other render modules
fotogrids/templates/save_as_template_button fotogrids/catalog/json_files takes $json_file_paths — the absolute paths to the settings-catalog JSON files that build the entire Gallery settings UI. Append a path and your own fields appear alongside the built-in ones.PHP catalog/json_files
PHP
add_filter( 'fotogrids/catalog/json_files', function ( array $paths ) {
$paths[] = plugin_dir_path( __FILE__ ) . 'catalog/my-fields.json';
return $paths;
} );
No. Every hook name and every JavaScript event is in Free. The live feature record reads “Developer API & Hooks — Free, Live now”. Free is already a full kit. Pro layers on the rest.
No. The convention is fotogrids/{category}/{subcategory}/{action} and it holds across the board. fg- and data-fg- are CSS class and attribute prefixes, not hooks.
Use the scoped variant. Every render hook fires three times — flat, then /{gallery|album}, then /{gallery|album}/{id} — with an identical callback signature. fotogrids/render/final_html/gallery/42 reaches Gallery 42 and nothing else.
Yes. Hook fotogrids/render/register_modules and call Module_Registry::register('layouts', My_Layout::class) with a class implementing FotoGrids\Render\Api\Layout. Layouts have no filters of their own — registering a module is how you extend them.
Every hook is declared as a constant on a hook-bag class under Plugin/src/includes/hooks/{actions,filters,js-events}/, and each one carries a docblock and an @since tag, so autocomplete resolves the constant and the docblock tells you the signature.
wp.hooks actions or filters of its own and no jQuery events. Every front-end event is a native CustomEvent, so addEventListener is the whole API. Lightbox, pagination and filter events fire on the Gallery element and bubble; the runtime events fire on document. Every hook name, every JavaScript event, and a render API Pro is built on. Nothing here is behind a licence key.
We use cookies and similar technologies to make our website work smoothly and understand how it’s used. Choosing “Accept” helps us personalize your experience. If you decline or change your mind later, some features may not work as expected.