This boilerplate provides a template to customize your own marketplace DAM app.
- Contentstack Account
Nodejs- v20.10.0 &NPM- v10.2.4
Reveal/Collapse the code structure
marketplace-dam-boilerplate-app
└── ui
├── babel.config.js
├── example
│ ├── bynder
│ │ ├── root_config
│ │ │ ├── ConfigScreen
│ │ │ │ └── index.tsx
│ │ │ ├── CustomField
│ │ │ │ └── index.tsx
│ │ │ ├── DamEnv
│ │ │ │ └── index.ts
│ │ │ ├── index.tsx
│ │ │ └── SelectorPage
│ │ │ └── index.tsx
│ │ └── rte_config
│ │ ├── DamEnv
│ │ │ └── index.tsx
│ │ ├── index.tsx
│ │ └── RTEFunctions
│ │ └── index.tsx
│ ├── cloudinary
│ │ ├── root_config
│ │ │ ├── ConfigScreen
│ │ │ │ └── index.tsx
│ │ │ ├── CustomField
│ │ │ │ └── index.tsx
│ │ │ ├── DamEnv
│ │ │ │ └── index.ts
│ │ │ ├── index.tsx
│ │ │ └── SelectorPage
│ │ │ └── index.tsx
│ │ └── rte_config
│ │ ├── DamEnv
│ │ │ └── index.tsx
│ │ ├── index.tsx
│ │ └── RTEFunctions
│ │ └── index.tsx
│ └── sample_dam_app
│ ├── root_config
│ │ ├── AssetData
│ │ │ └── index.tsx
│ │ ├── ConfigScreen
│ │ │ └── index.tsx
│ │ ├── CustomComponent
│ │ │ └── index.tsx
│ │ ├── CustomField
│ │ │ └── index.tsx
│ │ ├── DamEnv
│ │ │ └── index.ts
│ │ ├── index.tsx
│ │ ├── SelectorPage
│ │ │ └── index.tsx
│ │ ├── styles.scss
│ └── rte_config
│ ├── DamEnv
│ │ └── index.tsx
│ ├── index.tsx
│ └── RTEFunctions
│ └── index.tsx
├── jest.config.js
├── jest.CSStub.js
├── jest.setup.js
├── package.json
├── package-lock.json
├── public
│ ├── favicon.ico
│ └── index.html
├── rte
│ ├── custom.d.ts
│ ├── LICENSE
│ ├── package.json
│ ├── package-lock.json
│ ├── SECURITY.md
│ ├── src
│ │ ├── common
│ │ │ ├── constants
│ │ │ │ └── index.ts
│ │ │ ├── locale
│ │ │ │ └── en-us
│ │ │ │ └── index.ts
│ │ │ └── utils
│ │ │ └── index.ts
│ │ ├── components
│ │ │ ├── DAMIcon
│ │ │ │ └── index.js
│ │ │ ├── DeleteModal
│ │ │ │ └── index.js
│ │ │ ├── EmbedBtn
│ │ │ │ └── index.js
│ │ │ ├── ErrorImage
│ │ │ │ └── index.js
│ │ │ ├── ImageEditModal
│ │ │ │ └── index.js
│ │ │ ├── ImageElement
│ │ │ │ └── index.js
│ │ │ └── styles.scss
│ │ ├── dam.ts
│ │ ├── plugin.tsx
│ │ └── rte_config
│ │ ├── DamEnv
│ │ │ └── index.tsx
│ │ ├── index.tsx
│ │ └── RTEFunctions
│ │ └── index.tsx
│ ├── tsconfig.json
│ ├── webpack.common.js
│ ├── webpack.dev.js
│ └── webpack.prod.js
├── src
│ ├── common
│ │ ├── asset
│ │ │ ├── logo.svg
│ │ │ └── NoImg.svg
│ │ ├── constants
│ │ │ └── index.tsx
│ │ ├── contexts
│ │ │ ├── AppConfigContext.tsx
│ │ │ ├── ConfigStateContext.tsx
│ │ │ ├── CustomFieldContext.tsx
│ │ │ └── MarketplaceAppContext.tsx
│ │ ├── hooks
│ │ │ └── useAppLocation.ts
│ │ ├── locale
│ │ │ └── en-us
│ │ │ └── index.ts
│ │ ├── providers
│ │ │ ├── AppConfigProvider.tsx
│ │ │ ├── ConfigStateProvider.tsx
│ │ │ ├── CustomFieldProvider.tsx
│ │ │ └── MarketplaceAppProvider.tsx
│ │ ├── types
│ │ │ └── index.ts
│ │ └── utils
│ │ ├── ConfigScreenUtils.tsx
│ │ ├── CustomFieldUtils.tsx
│ │ ├── index.tsx
│ │ └── SelectorPageUtils.tsx
│ ├── components
│ │ ├── AppFailed
│ │ │ └── index.tsx
│ │ ├── DeleteModal
│ │ │ └── index.tsx
│ │ ├── ErrorBoundary
│ │ │ └── index.tsx
│ │ ├── Loaders
│ │ │ ├── ConfigLoader.tsx
│ │ │ ├── CustomFieldLoader.tsx
│ │ │ └── SelectorPage.tsx
│ │ ├── NoConfigImage
│ │ │ └── index.tsx
│ │ ├── NoImage
│ │ │ └── index.tsx
│ │ └── InfoMessage
│ │ └── index.tsx
│ ├── containers
│ │ ├── App
│ │ │ ├── index.tsx
│ │ │ └── styles.scss
│ │ ├── ConfigScreen
│ │ │ ├── Components.tsx
│ │ │ ├── index.tsx
│ │ │ └── MultiConfigModal.tsx
│ │ │ └── styles.scss
│ │ ├── CustomField
│ │ │ ├── AssetContainer.tsx
│ │ │ ├── Card
│ │ │ │ ├── AssetCardContainer.tsx
│ │ │ │ └── AssetCard.tsx
│ │ │ ├── index.tsx
│ │ │ ├── List
│ │ │ │ ├── AssetListContainer.tsx
│ │ │ │ └── AssetList.tsx
│ │ │ └── styles.scss
│ │ └── SelectorPage
│ │ ├── index.tsx
│ │ └── style.scss
│ ├── index.css
│ ├── index.tsx
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ ├── root_config
│ │ ├── Components
│ │ │ ├── CustomConfig.tsx
│ │ │ └── CustomSelector.tsx
│ │ ├── ConfigScreen
│ │ │ └── index.tsx
│ │ ├── CustomField
│ │ │ └── index.tsx
│ │ ├── DamEnv
│ │ │ └── index.ts
│ │ ├── index.tsx
│ │ ├── SelectorPage
│ │ │ └── index.tsx
│ │ ├── styles.scss
│ ├── services
│ │ └── index.ts
│ └── __tests__
│ ├── ConfigScreen
│ │ ├── ConfigScreen.spec.tsx
│ │ └── Utils.spec.tsx
│ ├── CustomField
│ │ ├── CustomField.spec.tsx
│ │ └── Utils.spec.tsx
│ └── SelectorPage
│ ├── SelectorPage.spec.tsx
│ └── Utils.spec.tsx
└── tsconfig.json-
To start developing your DAM app using this boilerplate, first clone the DAM Boilerplate GitHub repository and copy its contents into a new repository for your app.
-
The source folder of your new app repository will be referred to as APP_DIRECTORY from this point onward.
-
Open the package.json inside the ui and rte folders (
<APP_DIRECTORY>/ui/package.json&<APP_DIRECTORY>/ui/rte/package.json) and update the name field with your app's name. -
Open the main HTML file located at
<APP_DIRECTORY>/ui/public/index.htmland update the<title>tag with your app's name. -
Replace the default favicon.ico with your app-specific icon. The file is located at
<APP_DIRECTORY>/ui/public/favicon.ico.
-
.envfiles are required in both ui and ui/rte directories. Rename the existing.env.examplefiles to.envand add the following values. -
UI (<APP_DIRECTORY>/ui/.env) and JSON RTE (<APP_DIRECTORY>/ui/rte/.env) ENVIRONMENT VARIABLES
REACT_APP_CUSTOM_FIELD_URL = http://localhost:4000 REACT_APP_REGION_MAPPING='{"NA": { "JSON_RTE_URL": "https://rte-extension.contentstack.com" },"EU": { "JSON_RTE_URL": "https://eu-rte-extension.contentstack.com" },"AZURE_NA": { "JSON_RTE_URL": "https://azure-na-rte-extension.contentstack.com" },"AZURE_EU": { "JSON_RTE_URL": "https://azure-eu-rte-extension.contentstack.com" },"GCP_NA": { "JSON_RTE_URL": "https://gcp-na-rte-extension.contentstack.com" },"GCP_EU": { "JSON_RTE_URL": "https://gcp-eu-rte-extension.contentstack.com" },"AU": {"JSON_RTE_URL": "https://au-rte-extension.contentstack.com" }}'
- Open a terminal, navigate to your APP_DIRECTORY, and install the necessary packages :
cd <APP_DIRECTORY>
npm i
- Then, navigate to the ui directory and install its dependencies:
cd <APP_DIRECTORY>/ui
npm i
- After you install the dependencies, run the following command in the ui folder to get started:
-
For Linux / MacOS
npm run start -
For Windows
npm run winStart
The UI server will start on port 4000.
- Next, navigate to the rte folder and install the dependencies:
cd <APP_DIRECTORY>/ui/rte
npm i
- After you install the dependencies, run the following command in the rte folder to start the webpack server.
npm run start
The RTE webpack server will start on port 1268.
-
<MarketplaceAppProvider>: The MarketplaceAppProvider usesMarketplaceAppContextand initializes the Contentstack App SDK and shares its instance and configuration across components using the useContext hook, eliminating the need for prop drilling. -
<AppConfigProvider>: The AppConfigProvider usesAppConfigContextand generates dynamic state values based on the rootConfig's configuration and manages installation data, providing centralized state accessible via the useContext hook for child components. -
<ConfigStateProvider>: The ConfigStateProvider usesConfigStateContextand handles local state management for ConfigScreen UI Location, accessed by sub-components. -
<CustomFieldProvider>: The CustomFieldProvider usesCustomFieldContextand consolidates the management of state and rendering data, distributing it to multiple child components. It is responsible for both retrieving and updating this data.
useAppLocation: The useAppLocation hook retrieves the location instance from the app-sdk by accessing the MarketplaceAppContext. It returns the location name (e.g., "CustomField") and the location instance.
Each route corresponds to a specific UI location. Lazy loading route components are recommended to reduce the bundle size.
- ConfigScreen
(path = "/config") - CustomField
(path = "/custom-field") - SelectorPage
(path = "/selector-page")
- Create a new Route component inside route. Use default export
- Inside
ui/src/containers/App/index.tsx, lazy load the route component. Example:const ConfigScreen = React.lazy(() => import("../ConfigScreen")); - Add the route wrapped inside
Suspense. Example:<Route path="/config" element={ <Suspense fallback={<ConfigLoader />}> <AppConfigProvider> <ConfigScreen /> </AppConfigProvider> </Suspense> } />
- Inside
- This setup uses basic SCSS for styling
-
Visit Developer Hub on the following region of your preference:
-
Create a new app by clicking
+ New Appbutton at top right and Select app type asStack App, add Name and Description. The app will be initially private. To make an app public on Contextstack Marketplace, refer App Submission and Approval Guide. -
Once created, go to the Basic Information page and upload your app icon.
-
Open the UI Locations tab and add the URL of your app. For e.g. : https://localhost:4000
-
From Available location(s) , add App Configuration, Custom Field and JSON RTE.
-
For App Configuration, add path. The value of path is the route added for ConfigScreen in
<APP_DIRECTORY>/ui/src/containers/App/index.tsx. Also we are using HashRouter for routing. So the value of path should be/#/config. -
For Custom Field, add name and path. The value of path is the route added for CustomField in
<APP_DIRECTORY>/ui/src/containers/App/index.tsxi.e./#/custom-field. Select the Data Type asJSON. -
For JSON RTE, add the name and path. For this location, there won’t be a route path in
<APP_DIRECTORY>/ui/src/containers/App/index.tsx. We are serving webpack build file. For localhost, webpack file will be served ashttps://localhost:1268/dam.js. So the path should be/dam.js. For production environments, path should be/dist/dam.js.
For all locations, Switch on the toggle for
Signedif required. Switch on the toggle forEnabledto enable the location. Add theDescriptionif required.Note :-
- When hosting your app locally, you can't include a JSON RTE within the same app on the Developer Hub because it uses a different port. To work around this, after completion of the UI of the Configscreen, Customfield and Selectorpage, change the
App URLin your app and set it to "https://localhost:1268" and add the JSON RTE with the path "/dam.js." - The App will now work on a different port but use the same configuration saved on configscreen. Here, the Configscreen and Customfield location will not be visible.
-
-
Now install the app by clicking the Install App button at top right. From the next window, select the stack in which you want to install the app.
Note : Ensure the paths defined in
<APP_DIRECTORY>/ui/src/containers/App/index.tsxmatch exactly with those set in the Developer Hub UI Location settings.
After the app is installed, you can refer to the pages developed at various UI locations in the stack. Below are the various UI locations and their corresponding page in source code:
| UI Location | Page Source |
|---|---|
| Config Screen | <APP_DIRECTORY>/ui/src/containers/ConfigScreen/index.tsx |
| Custom Field | <APP_DIRECTORY>/ui/src/containers/CustomField/index.tsx |
| RTE | <APP_DIRECTORY>/ui/rte/src/plugin.tsx |
To create build for development, staging and production environments, we have added build.sh file at the location <APP_DIRECTORY>/build.sh
Command to run the build script is
bash build.sh <UI_URL>
This command takes one argument, which is the UI URL of the app. We are taking this argument to rename the app URL as per the environment in the .env files of ui and ui/rte.
After the execution of the build script, the build folder will get created inside the ui folder. Inside that build folder, there will be a dist folder which has the webpack output files for RTE location. Other folders and files from the build folder will be for Configuration and Custom Field locations.
If you don’t want to use build script, then use below command
-
To create build for ui, navigate to ui
cd <APP_DIRECTORY>/ui npm run build -
To create build for rte, navigate to rte
cd <APP_DIRECTORY>/ui/rte npm run build
You need to upload all the files from the build folder on AWS S3 or any static file hosting service of your preference.
To develop your Marketplace DAM App:
- Modify files in
root_configfor UI integration. - Modify files in
rte_configfor JSON RTE integration.
Refer to the TEMPLATE.md file located at <APP_DIRECTORY>/TEMPLATE.md, for more details.
For reference purpose, Examples of DAM Apps like Bynder and Cloudinary are added to the 'example' directory.
- Refer
<APP_DIRECTORY>/ui/example/bynder/root_configdirectory for UI implementation of Bynder App. - Refer
<APP_DIRECTORY>/ui/example/bynder/rte_configdirectory for JSON RTE implementation of Bynder App.