---
title: "Self-hosted Design Editor | Customer's Canvas On-Premises"
canonical: "https://customerscanvas.com/docs/on-premises/design-editor/iframe-api/quick-start/self-hosted-iframe/"
---

# Self-hosted Design Editor

> Let's assume that you have connected Design Editor up and running. This article explains how to open Design Editor on a page and load a template into it.

To do this, you need to load Design Editor to a page by adding an `<iframe>` element and loading the application into it through the [IFrame API](https://customerscanvas.com/docs/on-premises/design-editor/iframe-api/reference.md). During this process, you will specify a _product definition_ containing a reference to a template (typically created in Photoshop or InDesign) and an _editor configuration_ describing the features of Design Editor you want to use.

Let's walk through this process.

## Adding Design Editor to the page

On the page where you want to display an editor, you need to do the following: 

### Load the IFrame API JS library 

Among the scripts of your page, add the following line: 

```html title="Insert IFrame API JS library reference"
<!-- INSERT CUSTOMER'S CANVAS URL HERE!-->
<!-- To the base URL of your CC instance, add Resources/Generated/IframeApi.js -->
<script type="text/javascript" id="CcIframeApiScript"
    src="<DESIGN EDITOR URL>/Resources/Generated/IframeApi.js"></script>
```

The `<DESIGN EDITOR URL>` is the location of your Design Editor instance, which may look like **`https://my-site.com/design-editor/6.10.0`**.

Notice the `id="CcIframeApiScript"` part. Don't forget to add it to the `<script>` tag, otherwise the editor may fail to load.

### Add the `<iframe>`

Add the `<iframe>` element, which will contain the editor on the page. For example, it may look like this:

```html {4} title="Adding IFrame element"
<main class="main">
    <div class="main__section main__section-iframe">
        <div class="main__iframe">
            <iframe id="editorFrame"></iframe>
        </div>
    </div>
</main>
```

Most likely, you will want to add it to a modal dialog or occupy almost 100% of the screen, so feel free to adjust the CSS to achieve this, for example, like this:

```css title="Adjusting IFrame CSS"
.main {
    padding: 1.429rem;
}

.main__iframe {
    height: calc(100% - 8rem);
    width: 100%;
}

.main__iframe iframe {
    border: none;
    height: 100%;
    width: 0;
    min-width: 100%;
    position: relative;
}
```

### Load the editor to the IFrame

Now, let's use the IFrame API to load Design Editor to the IFrame. To achieve this, let's use the [`IframeApi.loadEditor`](https://customerscanvas.com/docs/on-premises/design-editor/iframe-api/reference/design-editor-iframe/functions/IframeApi.loadEditor.md) function as follows: 

```html title="Using the loadEditor"
<script>
    document.addEventListener('DOMContentLoaded', async () => {
        const product = { 
            /* product definition - see further */ 
        };  
        const config = {
            // ...
            // Few options will be considered further
        };

        // Customer's Canvas is loaded with this line. 
        var editor = await CustomersCanvas.IframeApi.loadEditor(
            document.getElementById("editorFrame"), product, config);

    });
</script>
```

As you can see, we are passing three parameters here - the IFrame element itself, the _product definition_, and the _config_ of the editor. 

## Working with designs  

### Loading a template

First, prepare an InDesign or Photoshop template. See the [designer's manual](https://customerscanvas.com/docs/hub/designers-manual/) for more details. For simplicity, you may download the [sample flyer design](pathname:///files/sample-flyer.zip).

Here, we assume that you didn't connected Design Editor to Customer's Canvas cloud platform (otherwise, see the [Cloud Design Editor](https://customerscanvas.com/docs/on-premises/design-editor/iframe-api/quick-start/cloud-iframe.md). If so, all your data should be stored on the same server with the application in a special folder, specified in the [Design Editor configuration](https://customerscanvas.com/docs/on-premises/design-editor/it-ops/config-backend/appsettings.md). By default, it is **../assets/designs**.

Once you have downloaded a sample design or created your own template, put it to this folder. If necessary, you may create subfolders of any depth here. Besides the designs, you also need to put all fonts used in this design to the **fonts** folder (may be changed in the [Design Editor configuration](https://customerscanvas.com/docs/on-premises/design-editor/it-ops/config-backend/design-atoms.md)).

Let's say you have created the **my-templates** subfolder in the **designs** folder and put your IDML design here. Modify your code as follows: 

```html {5} title="Loading design by product definition"
<script>
    document.addEventListener('DOMContentLoaded', async () => {
        const product = {
            surfaces: {
                file: "my-templates/flyer" // note, omit the file extension!
            }
        };
        const config = {
            // ...
        };

        var editor = await CustomersCanvas.IframeApi.loadEditor(
            document.getElementById("editorFrame"), product, config);

    });
</script>
```

As you can see, you need to create a JSON object called _product definition_ where you need to specify a path to the file relatively the **designs** folder without a file extension. You may learn about various kinds of the definitions in the [Product definition](https://customerscanvas.com/docs/on-premises/design-editor/iframe-api/product-definition.md) section.

### Saving a design 

You may have noticed that the `loadEditor` function returns an Editor object. It allows one to do many things with a design modified by a user. In particular, you may save the user's work as a _private design_.

To do so, we need to specify the name of the user you are working with.

```html {9} title="Specifying a user id"
<script>
    document.addEventListener('DOMContentLoaded', async () => {
        const product = {
            surfaces: {
                file: "my-templates/flyer" 
            }
        };
        const config = {
            userId: "12345"
        };

        var editor = await CustomersCanvas.IframeApi.loadEditor(
            document.getElementById("editorFrame"), product, config);

        // ... 
    });
</script>
```

:::warning
Once you pass a `userId`, you may get the ***User token must be provided*** error. This happens when you enable the secure mode (enabled by default in Customer's Canvas 5 and above). Without this protection, it would be possible to pass any username from JavaScript code, which may allow malicious actors to steal designs or uploads made by other users.

The best way to work with users is to use the special API in Design Editor to generate a token for a specific user, and pass it as a `tokenId` parameter along with the `userId`. See the [Security in Customer's Canvas section](https://customerscanvas.com/docs/on-premises/design-editor/apis/auth.md) in the IFrame API for details.

For the time being, to complete this tutorial, you may just turn off the secure mode by setting the `SecureModeEnabled` key in **AppSettings.config** of the Design Editor to `False`. However, it is strongly recommended not to do this for the production environment!
:::

Now, we need to use the `finishProductDesign` method of the Editor object to save the result. Let's add a button somewhere in HTML:

```html title="A button to save results"
<button class="header__button" id="finish" disabled="disabled">
    Save
</button>
```

and add this code:

```html {14-21} title="Save the design"
<script>
    document.addEventListener('DOMContentLoaded', async () => {
        const product = {
            surfaces: {
                file: "my-templates/flyer" 
            }
        };
        const config = {
            userId: "12345"
        };

        var editor = await CustomersCanvas.IframeApi.loadEditor(
            document.getElementById("editorFrame"), product, config);

        document.getElementById("finish").removeAttribute("disabled");
        document.getElementById('finish').addEventListener('click', async () => {
            let result = await editor.finishProductDesign();
            console.log("Pass to reopen the design (private design id and user id accordingly): ", result.stateId, result.userId);
            console.log("Links to print-ready files: ", result.hiResUrls);
            console.log("Links to proof images: ", result.proofImageUrls);
        });
    });
</script>
```

### Working with private designs

As you may notice, `finishProductDesign` returns an object with a `stateId` field that holds the design ID of a new design. You can just pass it as a product definition and it will reopen the user's design (provided that it is accompanied with a proper user ID), as in the example below.

When you call `finishProductDesign` without parameters, it will create a new copy of a private design on each call. You may not always want to have all copies of all private design versions. To overwrite the previous design on each save, just pass it as a `stateId` parameter of the options object passed to `finishProductDesign`.

```html {6,10,19} title="Load private design"
<script>
    document.addEventListener('DOMContentLoaded', async () => {
        // Let's imagine finish product design returned something like `9caeb6fc-ae91-4c66-a2c0-d6d9926ec57e`
        const product = {
            surfaces: {
                file: "my-templates/flyer" 
            }
        };
        const config = {
            userId: "12345"
        };

        var editor = await CustomersCanvas.IframeApi.loadEditor(
            document.getElementById("editorFrame"), product, config);

        document.getElementById("finish").removeAttribute("disabled");
        document.getElementById('finish').addEventListener('click', async () => {
            let result = await editor.finishProductDesign({
                stateId: product
            });
        });
    });
</script>
```

## Loading Fonts

Now that we know how to work with the designs, let's learn about fonts. Let's see how you can set up a list of fonts available for a user. 

By default, Design Editor will show all fonts it finds in the **fonts** folder, but you quite often don't want to show such a large list to a user. Instead, you may want to handpick several fonts that work best with the design the user edits. 

What you need is to set a list of the Postscript names of the necessary fonts to the `fontList` parameter of a config. You may find the Postscript name using your favorite font management software (e.g. a native Font Book app on Mac) or an online service (e.g. [FontDrop.info](https://fontdrop.info/)). You can do it as follows:

```html {10-17} title="Load font list"
<script>
    document.addEventListener('DOMContentLoaded', async () => {
        const product = {
            surfaces: {
                file: "my-templates/flyer" 
            }
        };
        const config = {
            initialMode: "Advanced",
            fontList: [
                "Montserrat-Regular",
                "Montserrat-Light",
                "Montserrat-ExtraBold",
                "OpenSans-Semibold",
                "OpenSans",
                "Roboto-Black"
            ]
        };

        var editor = await CustomersCanvas.IframeApi.loadEditor(
            document.getElementById("editorFrame"), product, config);

    });
</script>
```

:::note
The fonts used in the design are automatically added to this list.
:::

## Organizing Image Gallery

You may also want to provide your users with some standard clipart, branding materials (e.g. logos), and photos. The [Asset Manager](https://customerscanvas.com/docs/on-premises/design-editor/iframe-api/editor-configuration/image-manager.md) article in the Design Editor documentation explains all possible options of how you can use it to organize galleries, but for the purposes of this tutorial, let's just see how we can pick our images from the **images** folder of the Design Editor assets. 

Imagine that you are doing an editor for real estate agents and you want to provide them with the logos of the brokerage they work with. You will create a folder hierarchy in the **images** folder. There will be a **Logos** folder and subfolders. 

Let's set up the editor to show an **Add Logo** button which would allow users to choose a logo from the **Logos/Brokerage1** subfolder.
To do this, you need to set up several sections in the config: 

1. Initialize the image source. You need to use the **PublicSource** and specify the path to the folder. 
    :::note
    If you want to specify a root folder, use the `/` character or an empty string.
    :::

2. Configure the gallery (Asset Manager) widget - add a tab connected to the source you have just described and set up a few options. 
3. Configure a toolbox button, which would show the Asset Manager and add an image to the editor.

```html {13} title="Set up gallery"
<script>
    document.addEventListener('DOMContentLoaded', async () => {
        const product = {
            surfaces: {
                file: "my-templates/flyer" 
            }
        };
        const config = {
            assetSources: {
                Logos: {
                    type: "PublicSource",
                    rootCategory: {
                        name: "Logos/Brokerage1"
                    }
                }
            },
            widgets: {
                AssetManager: {
                    tabs: [{
                        name: "Logos",
                        assetSourceInstance: "Logos",
                        controls: {
                            categoriesEnabled: false,
                            assetNameEnabled: false,
                            toolbarEnabled: false
                        }
                    }]
                },
                Toolbox: {
                    buttons: [{
                        action: "Image",
                        iconClass: "cc_icon_clipart",
                        tabs: ["Logos"]
                    }
                    ]
                }
            }
        };

        var editor = await CustomersCanvas.IframeApi.loadEditor(
            document.getElementById("editorFrame"), product, config);

    });
</script>
```
