Cartble’s plugin architecture is designed to let developers extend the admin with new functionality while keeping all custom code self-contained and maintainable. This guide walks you through building a custom plugin from scratch — covering directory structure, the Bridge pattern, plugin registration, platform-aware data access, and state management conventions. TheDocumentation Index
Fetch the complete documentation index at: https://help.cartble.com/llms.txt
Use this file to discover all available pages before exploring further.
smart-price plugin at src/plugins/smart-price is the gold-standard reference implementation to study as you work through this guide.
Who this is for
This guide is written for developers who are comfortable with React, TypeScript, and Firestore. You should have access to the Cartble codebase and be familiar with how the admin shell is structured before building a plugin.Plugin directory structure
Every plugin lives in its own subdirectory undersrc/plugins/. All plugin-specific code — pages, hooks, services, types, and components — must be entirely self-contained within that directory. Nothing inside a plugin should import from another plugin’s directory.
What a plugin can do
A registered plugin can:- Add sidebar menu items — define one or more
adminAppsentries with icons, labels, and page components - Add full admin pages — React components rendered inside the admin shell when a sidebar item is clicked
- Inject UI slots — render components in predefined positions like product table columns or checkout flows
- React to platform hooks — execute logic when events like
on-checkout-startoron-order-placedfire - Read and write Firestore data — scoped to your platform’s collection path
The Bridge pattern
Plugins should not pollute the main application’s React context. Instead, wrap every exported page component with a Higher-Order Component (HOC) that provides the plugin’s own React Query client and localized settings. Thesmart-price plugin calls this withSmartPriceBridge.
withYourPluginBridge to every page component you register in adminApps. This ensures your plugin’s server-state management is completely isolated from the main application.
Plugin registration
Define and export your plugin as aPlugin object in your index.ts. The plugin registry reads this object to wire up sidebar navigation, slots, and hooks.
src/core/plugin-registry.ts so it is picked up by the PluginProvider.
Accessing platform context
Never hardcode aplatformId or build Firestore collection paths by hand. Always use the usePlatform hook to retrieve the current platform’s ID and scoped settings at runtime.
Data storage in Firestore
Store all plugin data under the platform-scoped path. Follow these conventions:| Data type | Firestore path |
|---|---|
| Products / primary assets | platforms/{platformId}/resources/ |
| Orders / transactions / logs | platforms/{platformId}/records/ |
| Plugin-private data | platforms/{platformId}/your_plugin_data/ |
smart_price_data, your_plugin_data) to avoid collisions with other plugins.
State management
Use React Query for all server-side state inside your plugin. Always scope your query keys withplatformId to prevent data from one platform leaking into another when a user switches accounts.
Reference implementation
Study thesmart-price plugin at src/plugins/smart-price for a complete example that demonstrates every pattern described in this guide — the Bridge HOC (SmartPriceBridge), full adminApps registration, Firestore service abstraction, scoped React Query keys, and UI component reuse from src/components/ui/.
