App Manifest
The app-manifest.json file is the central configuration for your GxP plugin. It defines settings, translatable strings, assets, and runtime state that the platform injects into your plugin.
File Location
The manifest file should be in your project root:
my-plugin/
├── app-manifest.json # <-- Here
├── src/
└── ...
Basic Structure
{
"settings": {},
"strings": {},
"assets": {},
"triggerState": {},
"dependencies": [],
"permissions": []
}
Configuration Sections
Settings (pluginVars)
Define configurable settings that administrators can customize per deployment:
{
"settings": {
"primary_color": "#FFD600",
"background_color": "#ffffff",
"company_name": "Acme Corp",
"max_items": 10,
"enable_animations": true
}
}
Access in your component:
const store = useGxpStore();
// Get a setting with fallback
const color = store.getSetting('primary_color', '#000000');
// Check if setting exists
if (store.pluginVars.enable_animations) {
// ...
}
Use in templates with the gxp-settings modifier:
<span gxp-string="company_name" gxp-settings>Default Company</span>
Strings (stringsList)
Define translatable text content:
{
"strings": {
"default": {
"welcome_title": "Welcome to the Event",
"welcome_subtitle": "Please check in below",
"button_checkin": "Check In",
"button_cancel": "Cancel",
"error_not_found": "Registration not found"
}
}
}
Use in templates with the gxp-string directive:
<h1 gxp-string="welcome_title">Default Welcome</h1>
<button gxp-string="button_checkin">Check In</button>
Access programmatically:
const store = useGxpStore();
const title = store.getString('welcome_title', 'Default Title');
Changes to strings in app-manifest.json are hot-reloaded during development. No page refresh needed!
Assets (assetList)
Define asset URLs (images, documents, etc.):
{
"assets": {
"hero_image": "/dev-assets/images/hero.jpg",
"logo": "/dev-assets/images/logo.png",
"background": "/dev-assets/images/bg-pattern.svg",
"welcome_video": "/dev-assets/videos/intro.mp4"
}
}
Use in templates with the gxp-src directive:
<img gxp-src="hero_image" src="/dev-assets/placeholder.jpg" alt="Hero" />
<img gxp-src="logo" src="/dev-assets/placeholder.jpg" alt="Logo" />
Access programmatically:
const store = useGxpStore();
const heroUrl = store.getAsset('hero_image', '/fallback.jpg');
Trigger State (triggerState)
Define dynamic runtime state that can change during plugin execution:
{
"triggerState": {
"is_active": true,
"current_step": 1,
"checked_in_count": 0,
"last_scan_result": null
}
}
Use in templates with the gxp-state modifier:
<span gxp-string="current_step" gxp-state>1</span>
<img gxp-src="dynamic_badge" gxp-state src="/placeholder.jpg" />
Update programmatically:
const store = useGxpStore();
// Update state
store.updateState('current_step', 2);
store.updateState('checked_in_count', store.triggerState.checked_in_count + 1);
// Read state
const step = store.getState('current_step', 1);
Dependencies
Dependencies define external API services your plugin can interact with. Each dependency maps API operations to endpoints that can be called via gxpStore.callApi().
Dependency Structure
{
"dependencies": [
{
"identifier": "access_points",
"model": "AccessPoint",
"permissionKey": "access_point",
"permissions": ["view_access_points", "manage_access_points"],
"operations": {
"access-points.index": "get:/v1/projects/{teamSlug}/{projectSlug}/access-points",
"access-points.show": "get:/v1/projects/{teamSlug}/{projectSlug}/access-points/{access_point}",
"access-points.store": "post:/v1/projects/{teamSlug}/{projectSlug}/access-points",
"access-points.update": "put:/v1/projects/{teamSlug}/{projectSlug}/access-points/{access_point}",
"access-points.destroy": "delete:/v1/projects/{teamSlug}/{projectSlug}/access-points/{access_point}"
},
"events": {
"AccessPointUpdated": "AccessPointUpdated",
"AccessPointDeleted": "AccessPointDeleted"
}
}
]
}
Dependency Fields
| Field | Type | Description |
|---|---|---|
identifier | string | Unique identifier for this dependency (used in callApi) |
model | string | The model/resource name from the API |
permissionKey | string | The permission key used for access control |
permissions | array | List of permissions required to use this dependency |
operations | object | Map of operationId to method:path (e.g., "get:/v1/...") |
events | object | Map of socket event names this dependency can emit/receive |
Using the Add Dependency Wizard
The easiest way to add dependencies is using the CLI wizard:
gxdev add-dependency
This interactive wizard will:
- Load the OpenAPI specification from the API
- Display available API tags/models
- Let you select which endpoints to include
- Let you select which socket events to include
- Generate the complete dependency configuration
- Add it to your
app-manifest.json
Calling Dependency APIs
Once defined, call any operation using gxpStore.callApi():
import { useGxpStore } from '@gx-runtime/stores/gxpPortalConfigStore';
const store = useGxpStore();
// List all access points
const accessPoints = await store.callApi('access-points.index', 'access_points');
// Get a specific access point
const accessPoint = await store.callApi('access-points.show', 'access_points', {
access_point: 123
});
// Create a new access point
const newAccessPoint = await store.callApi('access-points.store', 'access_points', {
name: 'Main Entrance',
location: 'Building A'
});
// Update an access point
await store.callApi('access-points.update', 'access_points', {
access_point: 123,
name: 'Updated Name'
});
// Delete an access point
await store.callApi('access-points.destroy', 'access_points', {
access_point: 123
});
The callApi method signature:
store.callApi(operationId, identifier, additionalData = {})
- operationId: The operation key from
operations(e.g.,'access-points.index') - identifier: The dependency identifier (e.g.,
'access_points') - additionalData: Object containing path parameters and/or request body data
Returns response.data from the API response.
Permissions
Define permissions required by the plugin:
{
"permissions": [
"camera",
"bluetooth",
"notifications"
]
}
Check permissions in code:
const store = useGxpStore();
if (store.hasPermission('camera')) {
// Enable camera features
}
Complete Example
{
"settings": {
"primary_color": "#FFD600",
"secondary_color": "#1976D2",
"company_name": "TechConf 2024",
"check_in_timeout": 30,
"enable_badge_printing": true
},
"strings": {
"default": {
"welcome_title": "Welcome to TechConf 2024",
"welcome_subtitle": "Scan your QR code to check in",
"btn_manual_entry": "Enter Code Manually",
"btn_help": "Need Help?",
"success_message": "You're all set!",
"error_invalid_code": "Invalid code. Please try again.",
"error_already_checked_in": "You've already checked in."
}
},
"assets": {
"logo": "/dev-assets/images/techconf-logo.png",
"hero_background": "/dev-assets/images/hero-bg.jpg",
"success_animation": "/dev-assets/animations/success.json"
},
"triggerState": {
"is_scanning": false,
"current_attendee": null,
"badge_printing": false
},
"dependencies": [],
"permissions": ["camera"]
}
Directive Reference
| Directive | Modifier | Source | Example |
|---|---|---|---|
gxp-string | (none) | stringsList | <h1 gxp-string="title">Default</h1> |
gxp-string | gxp-settings | pluginVars | <span gxp-string="company" gxp-settings>Acme</span> |
gxp-string | gxp-assets | assetList | <span gxp-string="logo_url" gxp-assets>/logo.png</span> |
gxp-string | gxp-state | triggerState | <span gxp-string="count" gxp-state>0</span> |
gxp-src | (none) | assetList | <img gxp-src="hero" src="/placeholder.jpg" /> |
gxp-src | gxp-state | triggerState | <img gxp-src="badge" gxp-state src="/placeholder.jpg" /> |
Best Practices
- Use descriptive keys -
welcome_titleis better thantitle1 - Provide defaults - Always include fallback text in your templates
- Group related strings - Keep related strings together for easier management
- Use dev-assets for development - Put placeholder images in
dev-assets/images/ - Keep settings minimal - Only expose settings that need admin configuration