Project Structure and Metadata
A Multi-Framework app lives in your Salesforce DX project as a UIBundle. The UIBundle represents your app’s UI and works with several other metadata types to define the app on the platform. In your project, its source code, build configuration, and metadata definition are stored together in a self-contained folder. When you deploy it, this metadata creates a UIBundle record that establishes the app’s identity on the Salesforce Platform and declares which containers can host it.
This topic describes the files and metadata that make up an app, whichever framework you use.
The Salesforce DX project structure includes a force-app/main/default folder that contains a uiBundles folder. Application metadata and content live together in this uiBundles directory. Each application subdirectory is a complete, self-contained unit with its own metadata definition (.uibundle-meta.xml), runtime configuration (ui-bundle.json), source code, and compiled assets.
Each app includes framework build files that connect your app to the platform:
vite.config.tsfor React appsangular.jsonand itsesbuild/andmiddleware/folders for Angular apps
The UIBundle metadata type can contain up to 2,500 files.
For more information, see Salesforce DX Project Structure and Source Format.
The .uibundle-meta.xml file, located in the force-app/main/default/uiBundles/{app_name} folder, defines the metadata for the app’s UI.
The target property in this file declares which kind of Salesforce container can use this UIBundle for its UI. It doesn’t surface anything on its own. Instead, a CustomApplication or a site references the UIBundle, and that container is what appears in the Salesforce UI.
CustomApplicationlets a CustomApplication, which is an internal, employee-facing app, use the UIBundle. This is the default target if you don’t specify a value. The CustomApplication appears in the App Launcher and opens in Salesforce, rendering its UI from the UIBundle.Experiencelets an Experience Cloud site use the UIBundle. The site appears in the Digital Experiences app and in the App Launcher, where selecting it opens the site URL in a new tab, and renders its UI from the UIBundle.
In both cases, additional metadata types are required to integrate your app with the Salesforce Platform and make it available in the UI. For example, to make an internal app available in the App Launcher, you also need a CustomApplication metadata type that references the UIBundle. See Internal and External App Files.
Here’s an example of the UI bundle metadata that’s deployed with an internal user app.
The isActive flag controls whether the deployed bundle is served, and version is a schema-version integer that starts at 1.
The ui-bundle.json file, located in the force-app/main/default/uiBundles/{app_name} folder, is the app’s runtime configuration file. The file can include these properties.
| Property | Type | Description |
|---|---|---|
outputDir | string | Required. The relative path to the directory that contains built application assets. The default value is “dist” for bundled apps. |
apiVersion | string | The Salesforce API version. Uses the format “vXX.X”; for example, “v66.0”. Be sure to use the same API version as the Salesforce org where you’re deploying your app. Defaults to the current org version. |
routing | string | Block containing custom URL handling. If omitted, standard file-based serving is used. |
routing.fileBasedRouting | boolean | When true, URLs are automatically mapped to the folder structure in outputDir. Defaults to true. |
routing.trailingSlash | string | Controls how the platform handles trailing slashes in URLs. Valid values are: • never: Removes the slash (for example, redirects “/page/” to “/page”). • always: Adds a slash (for example, redirects “/page” to “/page/”). • auto: No change. |
routing.fallback | string | Specifies the file to serve when a request doesn’t match any static asset or explicit rule. Set to index.html to allow client-side routing for single-page apps (SPAs). |
routing.rewrites | array | An array of rewrite rules that serve a different file without changing the browser URL. |
routing.rewrites.route | string | Required for rewrites. The source URL pattern. Supports named parameters (:id) and wildcards (*). |
routing.rewrites.target | string | Required for rewrites. The internal file path to serve. |
routing.redirects | array | An array of redirect rules that return an HTTP redirect response to the client. Rules are evaluated in order. |
routing.redirects.route | string | Required for redirects. The source URL pattern. Supports named parameters (:id) and wildcards (*). |
routing.redirects.target | string | Required for redirects. The destination URL. |
routing.redirects.statusCode | string | Required for redirects. The HTTP status for the redirect: • 301: Moved permanently (cacheable) • 302: Found (temporary, not cached) • 307: Temporary redirect (preserves request method, not cached) • 308: Permanent redirect (preserves request method, cacheable) |
This example shows all possible configuration options for a ui-bundle.json file.
This example shows a typical configuration file for a single-page app, which ensures that the app works correctly when a user refreshes the page on a sub-route (for example, /dashboard).
When you create an app, you choose an internal or an external template, which provides the necessary UIBundle project structure along with the related metadata that differs by use case. The template names differ by framework—for example, reactinternalapp and reactexternalapp for React, or angularinternalapp and angularexternalapp for Angular. For the full list of templates and the generate commands, see Generate an App from a Template.
Use an internal template to create internal-facing apps for employees signing in with their Salesforce credentials.
Projects based on an internal template also require the CustomApplication metadata. This type defines and configures a Salesforce app, and connects your app to the App Launcher. The force-app/main/default/applications folder contains the .app-meta.xml file, which includes a field called uiBundle. This field contains the fully qualified name of the UIBundle that represents your app, such as c__myapp.
When a CustomApplication references the UIBundle through its uiBundle field, and the UIBundle’s target is set to CustomApplication in the .uibundle-meta.xml file, the CustomApplication appears in the App Launcher and renders its UI from the UIBundle.
Similar to a standard Salesforce app, you can control user access to the app using profiles and permission sets. The internal templates also generate an <AppName> Access permission set that grants app visibility and the API Enabled permission. For more information, see Manage Your App and Grant User Access.
Use an external template to create external-facing apps for business-to-business (B2B) and business-to-consumer (B2C) use cases, such as partners or customers signing in from outside your org. The template provides a prebuilt shell that includes an Experience Cloud site with several pages, navigation, and authentication.
Projects based on an external template also require several additional metadata types to support the Experience Cloud site. The files are contained in these folders in force-app/main/default:
digitalExperienceConfigs: The DigitalExperienceConfig metadata type represents the configuration details for your org’s workspaces, such as the site label, site URL path prefix, and workspace type.digitalExperiences: The DigitalExperience metadata type represents a collection of related content items that form the site when combined with data from the DigitalExperienceConfig metadata type.networks: The Network metadata type represents an Experience Cloud site.sites: The CustomSite metadata type represents a Salesforce site.
Unlike a standard LWR site, the digitalExperiences folder contains only the sfdc_cms_site folder. In the content.json file in the folder:
- The
appContainerproperty is set totrueto indicate that the site is used in a Multi-Framework app. - The
appSpaceproperty refers to the related app. Make sure that the value uses the formatNamespacePrefix__DeveloperName(for example,MyNamespace__MyWebApporc__MyWebAppfor orgs without a registered namespace).
You can’t edit a Multi-Framework app’s Experience Cloud site in Experience Builder.
- Generate an App from a Template
- Manage Your App and Grant User Access
- Metadata API Developer Guide: UIBundle
- Salesforce DX Project Structure and Source Format