Skip to content

Media Providers ​

How the plugin gets media, and how to add a source of your own.

A provider is where a gallery's items come from. The Media Library is one. YouTube, Vimeo, TikTok, Wistia and plain external URLs are others. Adding another is one class and one registration call.

The contract ​

Every provider answers the same questions:

MethodReturns
get_id()A stable slug, stored on each item.
get_label()The name shown in the picker's source rail.
get_icon()Its glyph.
is_available()Whether it can run right now — credentials present, extension loaded.
is_pro()Whether it belongs to the premium build.
fetch()Media, as normalised items.
to_array()Its own description, for the picker.

The normalised item ​

fetch() returns items in one shape, whatever the source:

KeyHolds
idA stable identifier — an attachment id, or the URL for an external item.
typeimage or video.
urlWhat the tile displays.
thumbA small version.
fullThe full-size version, used by the lightbox.
width · heightIntrinsic dimensions. The measured layouts need these.
alt · title · caption · descriptionText.
linkA destination for the custom URL click action.
providerYour provider's id.

width and height matter more than they look

Justified and Masonry balance rows and pack columns from real proportions. A provider that returns items without dimensions will render, but those two layouts can't do their job.

Why this shape exists ​

The renderer only ever sees normalised items. It has no idea whether an item came from the Media Library or a third-party service. Neither does the React grid, the REST layer or the CSS.

That's what makes a new provider a genuinely additive change: one class, one registration, and it works in every layout, in the lightbox, with categories, with filtering and with pagination — with no change to anything downstream.

Registering one ​

php
add_action(
    'shaped_gallery_register_providers',
    function ( $registry ) {
        $registry::register( new My_Provider() );
    }
);

$registry is the provider registry class name. Registration happens during the plugin's own bootstrap, so the action is the right place — not init.

Appearing in the picker ​

The picker's source rail is built from the registry, so a registered, available provider shows up on its own. Two things decide whether yours appears:

  • is_available() returning true. If your provider needs an API key, return false until one is saved — a source that can't work shouldn't be offered.
  • is_pro() — which build it belongs to.

Things to get right ​

Sanitize everything you bring in. Anything from a remote service is untrusted. URLs go through esc_url_raw(), text through sanitize_text_field(), numbers get a real cast. Don't forward an unknown key — drop it.

Cast numbers, never absint() them. absint( -3 ) is 3, which turns nonsense into a plausible value. A bogus id that looks valid is worse than one that's obviously wrong.

Use wp_safe_remote_* with TLS verification on. An outbound request carrying a customer's credential isn't a place to switch verification off.

Cache your fetches, and cache failures too. Without a negative cache, a service that's down is re-dialled on every editor load, and each attempt costs the author a long timeout.

Read the response defensively. json_decode() of an HTML error page is null, and reading a property off it is a fatal error on PHP 8.

What providers can't do ​

Two features need a real WordPress attachment behind an item, so they don't apply to external providers:

Everything else — every layout, the lightbox, categories, filtering, search, pagination, click actions, hover animations — works for any provider.

Some of what an item carries belongs to the gallery, not the source: its categories, and its text overrides. When items are re-resolved from a provider at render time, that per-gallery data is carried across rather than being re-fetched — a provider could never know it.

If you're extending the resolution path, that's the rule to preserve. Dropping category membership silently empties every filter; dropping the text overrides means the source's own values quietly win over what the author typed.

Contributing categories from elsewhere ​

If you want memberships to come from a taxonomy or a provider's own albums rather than from the Edit Gallery modal, there's a filter for exactly that:

php
add_filter(
    'shaped_gallery_item_categories',
    function ( $categories, $item, $vocabulary ) {
        // Return category ids from wherever you like.
        // They're allow-listed against the gallery's own vocabulary.
        return $categories;
    },
    10,
    3
);

No renderer, CSS or runtime change is needed — filtering, counts and pagination all follow.

Where to go next ​