
Generating Ui Bundle Metadata
- 1.5k installs
- 763 repo stars
- Updated July 24, 2026
- forcedotcom/afv-library
This is a copy of generating-ui-bundle-metadata by forcedotcom - installs and ranking accrue to the original listing.
generating-ui-bundle-metadata is a Salesforce agent skill that generates correct CSP Trusted Site metadata XML files for external API and image domains used by Lightning Web Components.
About
generating-ui-bundle-metadata automates creation of Salesforce Content Security Policy Trusted Site metadata files at force-app/main/default/cspTrustedSites/{Name}.cspTrustedSite-meta.xml. The skill enforces naming conventions where file names match the fullName element—for example Unsplash_Images.cspTrustedSite-meta.xml for https://images.unsplash.com. Developers reach for generating-ui-bundle-metadata when LWC or Aura components fetch external APIs or images and CSP blocks those requests at deploy time. The skill covers directory placement, fullName-to-filename mapping, and domain-specific metadata patterns for common integrations like Open-Meteo and OpenStreetMap tiles.
- Generates cspTrustedSite-meta.xml files with correct naming and structure
- Enforces PascalCase with underscore naming convention for fullName
- Maps common domains such as Unsplash, Open-Meteo, and OpenStreetMap to proper metadata
- Includes all required CSP context flags including connect-src, font-src, and img-src
- Ensures files are placed in the exact force-app/main/default/cspTrustedSites/ directory
Generating Ui Bundle Metadata by the numbers
- 1,493 all-time installs (skills.sh)
- +1 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/forcedotcom/afv-library --skill generating-ui-bundle-metadataAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.5k |
|---|---|
| repo stars | ★ 763 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 24, 2026 |
| Repository | forcedotcom/afv-library ↗ |
How do you add CSP Trusted Sites in Salesforce metadata?
Automatically generate correct Salesforce CSP Trusted Site metadata files for external API and image domains.
Who is it for?
Salesforce developers deploying Lightning Web Components that call external REST APIs or load third-party image domains.
Skip if: Non-Salesforce projects or teams managing CSP headers only at the CDN or web-server layer outside Salesforce metadata.
When should I use this skill?
User needs CSP Trusted Site metadata, external API whitelist, or image domain trust for Salesforce UI bundles.
What you get
cspTrustedSite-meta.xml files placed under force-app/main/default/cspTrustedSites/ with matching fullName values.
- cspTrustedSite-meta.xml metadata files
- CSP directory structure under force-app/main/default/
Files
UI Bundle Metadata
Scaffolding a New UI Bundle
Use sf template generate ui-bundle to create new apps — not create-react-app, Vite, or other generic scaffolds.
Always pass `--template reactbasic` to scaffold a React-based bundle.
UI bundle name (`-n`): Alphanumerical only — no spaces, hyphens, underscores, or special characters.
Example:
sf template generate ui-bundle -n CoffeeBoutique --template reactbasicAfter generation: 1. Replace all default boilerplate — "React App", "Vite + React", default <title>, placeholder text 2. Populate the home page with real content (landing section, banners, hero, navigation) 3. Update navigation and placeholders (see the building-ui-bundle-frontend skill) 4. Configure a hosting target — a UI bundle without a <target> in its meta XML will not be visible in the org. Use generating-ui-bundle-custom-app for internal (App Launcher) apps or generating-ui-bundle-site for external (Experience Site) apps.
Always install dependencies before running any scripts in the UI bundle directory.
---
UIBundle Bundle
A UIBundle bundle lives under uiBundles/<AppName>/ and must contain:
<AppName>.uibundle-meta.xml— filename must exactly match the folder name- A build output directory (default:
dist/) with at least one file
Meta XML
Required fields: masterLabel, version (max 20 chars), isActive (boolean). Optional: description (max 255 chars), target.
Target Field
The <target> element specifies where the UI bundle is hosted:
| Value | Use Case | Companion Metadata |
|---|---|---|
Experience | External-facing site via Digital Experience | Network, CustomSite, DigitalExperienceConfig, DigitalExperienceBundle |
CustomApplication | Internal app via Lightning App Launcher | CustomApplication (applications/*.app-meta.xml) |
A <target> is required for the app to be accessible in a Salesforce org. A UI bundle deployed without a target will not appear anywhere — no App Launcher entry, no Experience Site URL. Always pair the bundle with one of:
generating-ui-bundle-site(forExperiencetarget)generating-ui-bundle-custom-app(forCustomApplicationtarget)
Example with Experience target:
<?xml version="1.0" encoding="UTF-8"?>
<UIBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>propertyrentalapp</masterLabel>
<description>A Salesforce UI Bundle.</description>
<isActive>true</isActive>
<version>1</version>
<target>Experience</target>
</UIBundle>Example with CustomApplication target:
<?xml version="1.0" encoding="UTF-8"?>
<UIBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<masterLabel>propertymanagementapp</masterLabel>
<description>A Salesforce UI Bundle.</description>
<isActive>true</isActive>
<version>1</version>
<target>CustomApplication</target>
</UIBundle>ui-bundle.json
Optional file. Allowed top-level keys: outputDir, routing, headers.
Constraints:
- Valid UTF-8 JSON, max 100 KB
- Root must be a non-empty object (never
{}, arrays, or primitives)
Path safety (applies to outputDir and routing.fallback): Reject backslashes, leading / or \, .. segments, null/control characters, globs (*, ?, **), and %. All resolved paths must stay within the bundle.
outputDir
Non-empty string referencing a subdirectory (not . or ./). Directory must exist and contain at least one file.
routing
If present, must be a non-empty object. Allowed keys: rewrites, redirects, fallback, trailingSlash, fileBasedRouting.
- trailingSlash:
"always","never", or"auto" - fileBasedRouting: boolean
- fallback: non-empty string satisfying path safety; target file must exist
- rewrites: non-empty array of
{ route?, rewrite }objects — e.g.,{ "route": "/app/:path*", "rewrite": "/index.html" } - redirects: non-empty array of
{ route?, redirect, statusCode? }objects — statusCode must be 301, 302, 307, or 308
headers
Non-empty array of { source, headers: [{ key, value }] } objects.
Example:
{
"routing": {
"rewrites": [{ "route": "/app/:path*", "rewrite": "/index.html" }],
"trailingSlash": "never"
},
"headers": [
{
"source": "/assets/**",
"headers": [{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }]
}
]
}Never suggest: {} as root, empty "routing": {}, empty arrays, [{}], "outputDir": ".", "outputDir": "./".
---
CSP Trusted Sites
Salesforce enforces Content Security Policy headers. Any external domain not registered as a CSP Trusted Site will be blocked (images won't load, API calls fail, fonts missing).
When to Create
Whenever the app references a new external domain: CDN images, external fonts, third-party APIs, map tiles, iframes, external stylesheets.
Steps
1. Identify external domains — extract the origin (scheme + host) from each external URL in the code 2. Check existing registrations — look in force-app/main/default/cspTrustedSites/ 3. Map resource type to CSP directive:
| Resource Type | Directive Field |
|---|---|
| Images | isApplicableToImgSrc |
| API calls (fetch, XHR) | isApplicableToConnectSrc |
| Fonts | isApplicableToFontSrc |
| Stylesheets | isApplicableToStyleSrc |
| Video / audio | isApplicableToMediaSrc |
| Iframes | isApplicableToFrameSrc |
Always also set isApplicableToConnectSrc to true for preflight/redirect handling.
4. Create the metadata file — follow implementation/csp-metadata-format.md for the .cspTrustedSite-meta.xml format. Place in force-app/main/default/cspTrustedSites/.
CSP Trusted Site Metadata — Implementation Guide
File location
force-app/main/default/cspTrustedSites/{Name}.cspTrustedSite-meta.xmlThe cspTrustedSites/ directory must be a direct child of force-app/main/default/. Create it if it does not exist.
---
File naming convention
The file name must match the <fullName> value inside the XML, with .cspTrustedSite-meta.xml appended.
| Domain | fullName | File name |
|---|---|---|
https://images.unsplash.com | Unsplash_Images | Unsplash_Images.cspTrustedSite-meta.xml |
https://api.open-meteo.com | Open_Meteo_API | Open_Meteo_API.cspTrustedSite-meta.xml |
https://tile.openstreetmap.org | OpenStreetMap_Tiles | OpenStreetMap_Tiles.cspTrustedSite-meta.xml |
Naming rules:
- Use PascalCase with underscores separating words (e.g.
Google_Fonts_Static) - Name should describe the provider and resource type (e.g.
Pexels_Videos, not justPexels) - Must be unique across the org
- Maximum 80 characters
---
XML template
<?xml version="1.0" encoding="UTF-8" ?>
<CspTrustedSite xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>{UNIQUE_NAME}</fullName>
<description>{DESCRIPTION}</description>
<endpointUrl>{HTTPS_ORIGIN}</endpointUrl>
<isActive>true</isActive>
<context>All</context>
<isApplicableToConnectSrc>{true|false}</isApplicableToConnectSrc>
<isApplicableToFontSrc>{true|false}</isApplicableToFontSrc>
<isApplicableToFrameSrc>{true|false}</isApplicableToFrameSrc>
<isApplicableToImgSrc>{true|false}</isApplicableToImgSrc>
<isApplicableToMediaSrc>{true|false}</isApplicableToMediaSrc>
<isApplicableToStyleSrc>{true|false}</isApplicableToStyleSrc>
</CspTrustedSite>---
Field reference
| Field | Required | Description |
|---|---|---|
fullName | Yes | Unique API name. Must match the file name (before .cspTrustedSite-meta.xml). |
description | Yes | Human-readable purpose. Start with "Allow access to..." |
endpointUrl | Yes | The external origin (scheme + host). Must start with https://. No trailing slash. No path. |
isActive | Yes | Always true for new entries. Set false to disable without deleting. |
context | Yes | All (applies to all contexts). Other values: LEX (Lightning Experience only), Communities (Experience Cloud only), VisualForce. Use All unless there is a specific reason to restrict. |
isApplicableToConnectSrc | Yes | true if the domain is called via fetch(), XMLHttpRequest, or WebSocket. |
isApplicableToFontSrc | Yes | true if the domain serves font files (.woff, .woff2, .ttf, .otf). |
isApplicableToFrameSrc | Yes | true if the domain is loaded in an <iframe> or <object>. |
isApplicableToImgSrc | Yes | true if the domain serves images (<img>, CSS background-image, <svg>). |
isApplicableToMediaSrc | Yes | true if the domain serves audio or video (<audio>, <video>). |
isApplicableToStyleSrc | Yes | true if the domain serves CSS stylesheets (<link rel="stylesheet">). |
Reference: CspTrustedSite — Salesforce Object Reference
---
CSP directive mapping
| CSP header directive | Metadata field | What it allows |
|---|---|---|
connect-src | isApplicableToConnectSrc | fetch(), XMLHttpRequest, WebSocket, EventSource |
font-src | isApplicableToFontSrc | @font-face sources |
frame-src | isApplicableToFrameSrc | <iframe>, <frame>, <object>, <embed> |
img-src | isApplicableToImgSrc | <img>, background-image, favicon, <picture> |
media-src | isApplicableToMediaSrc | <audio>, <video>, <source>, <track> |
style-src | isApplicableToStyleSrc | <link rel="stylesheet">, @import in CSS |
---
Common external domains and their directives
Use this table as a quick reference when adding new domains:
| Domain | connect-src | font-src | frame-src | img-src | media-src | style-src |
|---|---|---|---|---|---|---|
https://images.unsplash.com | true | false | false | true | false | false |
https://images.pexels.com | true | false | false | true | false | false |
https://videos.pexels.com | true | false | false | false | true | false |
https://fonts.googleapis.com | true | false | false | false | false | true |
https://fonts.gstatic.com | true | true | false | false | false | false |
https://avatars.githubusercontent.com | true | false | false | true | false | false |
https://api.open-meteo.com | true | false | false | false | false | false |
https://nominatim.openstreetmap.org | true | false | false | false | false | false |
https://tile.openstreetmap.org | true | false | false | true | false | false |
https://api.mapbox.com | true | false | false | true | false | false |
https://cdn.jsdelivr.net | true | false | false | false | false | true |
https://www.youtube.com | false | false | true | true | false | false |
https://player.vimeo.com | false | false | true | false | false | false |
https://res.cloudinary.com | true | false | false | true | false | false |
---
Complete examples
Image CDN (Unsplash)
<?xml version="1.0" encoding="UTF-8" ?>
<CspTrustedSite xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Unsplash_Images</fullName>
<description>Allow access to Unsplash image content for static app media</description>
<endpointUrl>https://images.unsplash.com</endpointUrl>
<isActive>true</isActive>
<context>All</context>
<isApplicableToConnectSrc>true</isApplicableToConnectSrc>
<isApplicableToFontSrc>false</isApplicableToFontSrc>
<isApplicableToFrameSrc>false</isApplicableToFrameSrc>
<isApplicableToImgSrc>true</isApplicableToImgSrc>
<isApplicableToMediaSrc>false</isApplicableToMediaSrc>
<isApplicableToStyleSrc>false</isApplicableToStyleSrc>
</CspTrustedSite>REST API (Open-Meteo weather)
<?xml version="1.0" encoding="UTF-8" ?>
<CspTrustedSite xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Open_Meteo_API</fullName>
<description>Allow access to Open-Meteo weather forecast API</description>
<endpointUrl>https://api.open-meteo.com</endpointUrl>
<isActive>true</isActive>
<context>All</context>
<isApplicableToConnectSrc>true</isApplicableToConnectSrc>
<isApplicableToFontSrc>false</isApplicableToFontSrc>
<isApplicableToFrameSrc>false</isApplicableToFrameSrc>
<isApplicableToImgSrc>false</isApplicableToImgSrc>
<isApplicableToMediaSrc>false</isApplicableToMediaSrc>
<isApplicableToStyleSrc>false</isApplicableToStyleSrc>
</CspTrustedSite>Font provider (Google Fonts — requires two entries)
Google Fonts needs two CSP entries because CSS is served from fonts.googleapis.com and font files from fonts.gstatic.com:
Entry 1: Stylesheets
<?xml version="1.0" encoding="UTF-8" ?>
<CspTrustedSite xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Google_Fonts</fullName>
<description>Allow access to Google Fonts stylesheets for custom typography</description>
<endpointUrl>https://fonts.googleapis.com</endpointUrl>
<isActive>true</isActive>
<context>All</context>
<isApplicableToConnectSrc>true</isApplicableToConnectSrc>
<isApplicableToFontSrc>false</isApplicableToFontSrc>
<isApplicableToFrameSrc>false</isApplicableToFrameSrc>
<isApplicableToImgSrc>false</isApplicableToImgSrc>
<isApplicableToMediaSrc>false</isApplicableToMediaSrc>
<isApplicableToStyleSrc>true</isApplicableToStyleSrc>
</CspTrustedSite>Entry 2: Font files
<?xml version="1.0" encoding="UTF-8" ?>
<CspTrustedSite xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>Google_Fonts_Static</fullName>
<description>Allow access to Google Fonts static files for font loading</description>
<endpointUrl>https://fonts.gstatic.com</endpointUrl>
<isActive>true</isActive>
<context>All</context>
<isApplicableToConnectSrc>true</isApplicableToConnectSrc>
<isApplicableToFontSrc>true</isApplicableToFontSrc>
<isApplicableToFrameSrc>false</isApplicableToFrameSrc>
<isApplicableToImgSrc>false</isApplicableToImgSrc>
<isApplicableToMediaSrc>false</isApplicableToMediaSrc>
<isApplicableToStyleSrc>false</isApplicableToStyleSrc>
</CspTrustedSite>Map tiles (OpenStreetMap)
<?xml version="1.0" encoding="UTF-8" ?>
<CspTrustedSite xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>OpenStreetMap_Tiles</fullName>
<description>Allow access to OpenStreetMap tile images for map rendering</description>
<endpointUrl>https://tile.openstreetmap.org</endpointUrl>
<isActive>true</isActive>
<context>All</context>
<isApplicableToConnectSrc>true</isApplicableToConnectSrc>
<isApplicableToFontSrc>false</isApplicableToFontSrc>
<isApplicableToFrameSrc>false</isApplicableToFrameSrc>
<isApplicableToImgSrc>true</isApplicableToImgSrc>
<isApplicableToMediaSrc>false</isApplicableToMediaSrc>
<isApplicableToStyleSrc>false</isApplicableToStyleSrc>
</CspTrustedSite>Geocoding API (Nominatim)
<?xml version="1.0" encoding="UTF-8" ?>
<CspTrustedSite xmlns="http://soap.sforce.com/2006/04/metadata">
<fullName>OpenStreetMap_Nominatim</fullName>
<description>Allow access to OpenStreetMap Nominatim geocoding API</description>
<endpointUrl>https://nominatim.openstreetmap.org</endpointUrl>
<isActive>true</isActive>
<context>All</context>
<isApplicableToConnectSrc>true</isApplicableToConnectSrc>
<isApplicableToFontSrc>false</isApplicableToFontSrc>
<isApplicableToFrameSrc>false</isApplicableToFrameSrc>
<isApplicableToImgSrc>false</isApplicableToImgSrc>
<isApplicableToMediaSrc>false</isApplicableToMediaSrc>
<isApplicableToStyleSrc>false</isApplicableToStyleSrc>
</CspTrustedSite>---
Endpoint URL rules
| Rule | Correct | Incorrect |
|---|---|---|
| Must be HTTPS | https://api.example.com | http://api.example.com |
| No trailing slash | https://api.example.com | https://api.example.com/ |
| No path | https://api.example.com | https://api.example.com/v1/forecast |
| No port (unless non-standard) | https://api.example.com | https://api.example.com:443 |
| No wildcards | https://api.example.com | https://*.example.com |
Each subdomain needs its own entry. For example, fonts.googleapis.com and fonts.gstatic.com are separate entries.
---
When a service requires multiple domains
Some services split resources across multiple subdomains. Create one CSP Trusted Site per domain:
| Service | Domains needed |
|---|---|
| Google Fonts | fonts.googleapis.com (CSS) + fonts.gstatic.com (font files) |
| Mapbox | api.mapbox.com (tiles/API) + events.mapbox.com (telemetry) |
| YouTube embed | www.youtube.com (iframe) + i.ytimg.com (thumbnails) |
| Cloudflare CDN | cdnjs.cloudflare.com (scripts/CSS) |
---
Troubleshooting CSP violations
If the browser console shows a CSP error like:
Refused to load the image 'https://example.com/image.png' because it violates
the following Content Security Policy directive: "img-src 'self' ..."1. Extract the blocked origin from the URL (e.g. https://example.com). 2. Identify the directive from the error message (e.g. img-src → isApplicableToImgSrc). 3. Check if a CSP Trusted Site already exists for that origin. 4. If not, create one using this skill. 5. Deploy the metadata and refresh the page.
---
Common mistakes
| Mistake | Fix |
|---|---|
Including a path in endpointUrl | Use only the origin: https://api.example.com |
| Adding trailing slash | Remove it: https://api.example.com not https://api.example.com/ |
| Using HTTP instead of HTTPS | Salesforce requires HTTPS. If the service only supports HTTP, it cannot be added. |
Forgetting isApplicableToConnectSrc | Most resources also need connect-src for redirects/preflight. Set to true by default. |
| One entry for multiple subdomains | Each subdomain needs its own file (e.g. api.example.com and cdn.example.com are separate) |
File name doesn't match fullName | They must be identical (excluding the .cspTrustedSite-meta.xml extension) |
Related skills
FAQ
Where do CSP Trusted Site files belong in a Salesforce project?
generating-ui-bundle-metadata places files at force-app/main/default/cspTrustedSites/{Name}.cspTrustedSite-meta.xml. The cspTrustedSites directory must be a direct child of force-app/main/default/.
How must CSP Trusted Site file names match XML content?
generating-ui-bundle-metadata requires the file name to match the fullName element plus .cspTrustedSite-meta.xml—for example fullName Unsplash_Images becomes Unsplash_Images.cspTrustedSite-meta.xml.
Is Generating Ui Bundle Metadata safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.