How I built a shared demo library for my WordPress plugins

By Monowar Hossain Case study

Summary. Every plugin I maintain has a "Demo Library" feature that lets users import ready-made content in one click. Instead of bundling those demos inside each plugin, I built one shared, free, static library — JSON plus images — hosted on GitHub Pages, which every plugin fetches from. Here is the problem it solves, how it is structured, and what I would do differently.

The problem

When a WordPress plugin ships pre-built demo content, the usual approach is to bundle it inside the plugin's own ZIP — images, JSON, everything.

That means every time you want to add one new demo, you have to cut a whole new plugin release, bump the version, and wait for WordPress.org to process it. It also bloats the plugin package with images most users will never import.

The solution

I built wp-plugin-demo-library — a repository that contains only demo resources, JSON manifests and images, served for free over GitHub Pages and consumed by each plugin's built-in Demo Library screen.

New demos can be added or updated at any time, with no plugin release at all.

It deliberately contains zero plugin code. Nothing in this repository is ever bundled into a WordPress.org package. It is pure content infrastructure.

How it is structured

wp-plugin-demo-library/
├── general-slider/
│   ├── demo-library.json   # manifest: list of demos + metadata
│   ├── demos/              # one JSON per demo
│   ├── previews/           # card thumbnails
│   └── assets/             # images, fonts, svg, video
└── advanced-testimonial/
    ├── demo-library.json
    ├── demos/
    ├── previews/
    └── assets/

Each plugin gets its own top-level folder, so adding support for a new plugin is just adding a new folder. There is no shared code to touch.

How a plugin consumes it

Each plugin fetches its own demo-library.json server-side, caches it for about six hours, and renders one card per demo. Demos are version-gated by a requires field, so an older plugin install is never offered a demo that needs a newer feature.

On import, the plugin downloads that demo's JSON, sideloads every image straight into the site's media library, and creates the actual content — sliders, testimonials, and so on.

The connection only ever happens when a user opens the Demo Library screen or clicks Import. It is a plain, read-only file request, and it is disclosed to users as an external service in each plugin's readme.txt.

This is what the user actually sees. Two different plugins, two different sets of demos — but both screens are reading from the same repository, and neither plugin ships a single demo image in its own ZIP.

The Demo Library screen inside Advanced Testimonial, showing demo sets grouped by industry with an Import Demo button on each card
Advanced Testimonial's Demo Library — the category filters and cards are built from demo-library.json.
The Demo Library screen inside Free Widgets For Elementor, listing ready-made Elementor sections with a one-click Import button
The same screen in Free Widgets For Elementor. Different demos, same repository, no shared code between the two plugins.

What I would do differently

If I were starting this over, I would standardise the demo JSON shape across plugins from day one. Right now each plugin defines its own shape — a slider's slides array looks nothing like a testimonial's testimonials array.

That is fine at two plugins. It will need a shared convention as more plugins join, and retrofitting one is more work than agreeing on it up front.

FAQ

Does this slow down the plugins that use it?
No. It is only contacted when a user actively opens the Demo Library screen or imports a demo, and it is a plain static file request to GitHub Pages, not an API with any processing.
Is any of my site data sent to this service?
No. It is a one-way, read-only download of public demo files — JSON and images. Nothing about your site is ever sent to it.
Can I add a demo without releasing a new plugin version?
Yes, that is the entire point. Commit new JSON and images to the repository and they appear in the plugin's Demo Library once the manifest cache expires, or after a manual refresh.

Conclusion

Splitting demo content out of the plugin release cycle turned out to be a small architectural decision with an outsized payoff. New demos ship in minutes instead of days, and none of my plugins carry image weight they do not need in their WordPress.org package.

Related guides

Written by Monowar Hossain — WordPress Developer specializing in custom themes, plugins, Elementor, ACF, and performance optimization. Open Source Contributor. Also known online as devmonowar.

Published 11 August 2026 · Last updated 11 August 2026