---
title: "Structure | Customer&#x27;s Canvas Hub"
canonical: "https://customerscanvas.com/docs/hub/admin-guide/workflows/ui-framework/structure/"
---

# Structure

> When customers edit a product, they will go through customization steps. This customization process is called a _workflow_. Usually, it contains an editor at the first step and a preview of results at the final step. All these steps are described in a workflow file. You can configure these files to create the workflow for your product. Read the [Creating and editing workflows](https://customerscanvas.com/docs/hub/admin-guide/workflows/ui-framework/editing.md) article, to learn where workflow files are in your Customer's Canvas account.

In this article, you will learn the structure of a workflow file. These are the basic elements of the top level of any workflow.

```json
{
  "attributes": [],
  "vars": {},
  "widgets": [],
  "steps": []
}
```

Usually, when defining a workflow, you can't do without the `Widgets` and `Steps` elements. Let's consider each of them.

## Widgets

A widget is the smallest element in a workflow file. A widget can display, change, or exchange some data. For example, a checkbox, a gallery, and the editor are widgets. Widgets can be visual and non-visual. Read more about the widget types and widget anatomy in the [Widgets](https://customerscanvas.com/docs/hub/admin-guide/workflows/ui-framework/widgets.md) articles.

In the following example, two widgets are defined. The `my-input-text` widget creates a field to input some text, and the `my-checkbox` widget displays a checkbox.

```json
{
  "widgets": [
    {
      "name": "my-input-text",
      "type": "input-text",
      "params": {
        "placeholder": "Type here...",
      }
    },
    {
      "name": "my-checkbox",
      "type": "checkbox",
      "params": {
        "value": false,
        "prompt": "Click the checkbox"
      }
    }
  ]
}
```

To use these definitions in a workflow, you must refer to these widgets in _steps_. To do this, you need to add a widget name to a panel displayed in a step. To know how it works, read the next paragraph about `steps`.

## Steps

The personalization process is divided into _steps_. You can consider a step as a screen of a multistep personalization wizard. At each step, customers go through one action: editing, selecting variants, or approving results. A step in turn is divided into parts called _panels_. A panel is a widget container on the screen.

The following are different types of panels.

|Panel name|Description|
|--|--|
|`topPanel`|workflow title and navigation|
|`mainPanel`|center panel for the main widgets|
|`toolPanel`|left panel for auxiliary widgets|
|`rightToolPanel`|right panel for auxiliary widgets|
|`bottomPanel`|contains tables or approval controls|

This is an example of a real workflow with highlighted panels.

![The real workflow with highlighted panels.](./images/structure-steps-panels.png)

Let's see an example of how to embed a widget in the `mainPanel` when defining a step. To do so, just use the widget name in the `name` property of the panel.

```json
{
  "widgets": [
    {
      "name": "introduction",
      "type": "static-text",
      "params": {
        "text": "No options available for this product."
      }
    }
  ],
  ...
  "steps": [
    {
      "title": "Step 1",
      "name": "Step 1",
      "mainPanel": {
        "name": "introduction"
      }
    }
  ]
}
```

This is how this widget in the step looks in the user interface.

![Steps with the static text widget](./images/steps-widget-static-text.png)

### Embedding several widgets in a panel

If you want to display more than one widget in a panel, use the `group` widget, which combines several widgets together and embeds them in one panel. Let's see the example.

![The group widget to embed many option widgets inside.](./images/structure-group-widget.gif)

To learn about the `group` widget in detail, read the [Group widget](https://customerscanvas.com/docs/hub/admin-guide/workflows/ui-framework/widgets/group.md) article.

## Vars

In a workflow file, you may need to use some data in several places. For this purpose, you can define `vars`. It works the following way: a value, object, or array is assigned to a variable. Then, you can use this variable in your workflow file, for example, in dynamic expressions. Using shorter expressions, you can avoid syntax mistakes, and a workflow file becomes more convenient to read.

All the variables are defined in the `vars` object. To use their values in your workflow file, enclose variable in curly brackets `{{...}}`.

For example, you may want to define some JS code in `vars` and then use `vars` in the `static-text` widget.

If you decided to change the JS code, you would need to change it only in `vars`, not in the widget.

```json
{
  "vars": {
    "basicVar": "{{ 1 + 1 }}",
  },
  ...
  "widgets": [
    {
      "name": "text1",
      "type": "static-text",
      "params": {
        "text": "{{vars.basicVar}}"
      }
    }
  ],
  ...
  "steps": [
    {
      "title": "Step 1",
      "name": "Step 1",
      "mainPanel": {
        "name": "text1"
      }
    }
  ]
}
```

This is how the result looks.

![The result from a variable is in the static-text widget.](./images/structure-vars-basic-result.png)

Let's consider another example of using variables. Let's say, you have a drop-down list with four strings in the `Option` widget. For this list, you can create an array in `vars`, and then embed it in `params`. If you need to change something, you just change them in `vars`, not in the widget.

```json
{
  "vars": {
    "list": [
      "Lime Green",
      "Chili Red",
      "Snow White",
      "Salmon"
    ],
  },
  ...
  "widgets": [
    {
      "name": "list-from-vars",
      "type": "option",
      "params": {
        "type": "radio",
        "values": {
          "{{#each vars.list as colorValue}}": {
            "title": "{{colorValue}}"
          }
        }
      }
    }
  ],
  ...
  "steps": [
    {
      "title": "Step 3",
      "name": "Step 3",
      "mainPanel": {
        "name": "list-from-vars"
      }
    }
  ]
}
```

![The option widget with variables options.](./images/structure-vars-drop-down-list.png)

## Attributes

When creating a real-life workflow file, you will need to refer to a design, a mockup, a background, or another kind of asset that you want to load in the editor. To avoid specifying the asset name or ID in a workflow file, you can declare a _product attribute_ in your workflow.

When you create a workflow file with attributes, it will look like this. First, you need to define the attributes in a workflow file. Then, create a product using this workflow in Product Specification and attach designs, mockups, and so on.

You can reuse this workflow file for other products with the same attributes.

This is how attaching attributes looks in Product Specification.

![Attributes in Product Specification](./images/attributes-in-pc.gif)

:::note
Attributes exist only in Product Specification. In the PIM module, it works by creating options and values and connecting designs to them. To learn more, read the [Product specification](https://customerscanvas.com/docs/hub/admin-guide/product-specification.md) sections.
:::

To create an attribute in your workflow file, add it to the `attributes` section. You can provide the attribute type, name, title, description, and other information there.

For example, the following code adds a `Design` attribute, which allows a user to select a design file.

```json
{
  "attributes": [{
    "name": "Design File",
    "label": "Design",
    "type": "single-asset",
    "assetType": "design",
    "description": "Choose a design associated with this product. Your design file can contain text, shapes, images, and backgrounds.",
    "validations": [
      {
        "type": "required",
        "message": "Choose a design which should be used as a template."
      }
    ]
  }]
}
```

To refer to this attribute in a workflow file, you need to use a dynamic expression like this:

```json
"{{ product.attributes.find(x=>x.name==='Design File')?.value?.id }}"
```

You may find it a bit awkward to add this expression every time you need to read the attributes. Let's add this to the `vars` block.

```json
"vars": {
  "myAttrib": "{{ product.attributes.find(x=>x.name==='Design File')?.value?.id }}"
}
```

As a result, you can refer to the `Design File` attribute in a widget using a shortcut from `vars`.

```json
{
  "widgets": [
    {
      "name": "label",
      "type": "static-text",
      "params": {
        "text": "{{ vars.myAttrib }}"
      }
    }
  ]
}
```

To read more about attributes, see the [Attributes](https://customerscanvas.com/docs/hub/admin-guide/workflows/ui-framework/attributes.md) article.

## Optional elements

In addition to the objects described above, let's look at optional elements of workflow files that affect the user interface.

### Logo

You can add a `logo` to the root level of your workflow file.

```json
{
  "logo": {
    "src": "https://example.com/images/logo.svg",
    "url": "https://example.com/"
  },
  "attributes": [],
  "vars": [],
  "widgets": [],
  "steps": []
}
```

### Mobile interface

You can also switch the user interface to the mobile view. In this view, you can see only the main panel, and all the others are hidden in the menu.

![UI framework with default settings in mobile version.](./images/ui-framework-mobile-layout.png)

To make all the panels visible, you need to add a section to a workflow file. It looks the following way.

![UI framework shows all the panels in the vertical mobile layout.](./images/ui-framework-panels-verticalmobile.png)

```json
{
  "layout": "verticalmobile",
  "attributes": [],
  "vars": [],
  "widgets": [],
  "steps": []
}
```

Then, you can manage the size of panels and their location through the styles.

```json
"style": {
    "--theme-vertical-mobile-left-order": 20,
    "--theme-vertical-mobile-main-order": 30,
    "--theme-vertical-mobile-main-height": "50vh"
},
```

### Saved projects

UI Framework allows customers to save projects at any stage of design editing and return to them later. Saved projects are only available to BackOffice customers and are product-specific: a project saved for Product A can only be opened for Product A, not for Product B.

#### Adding project buttons

To add **My projects** and **Save project** buttons to the top panel, specify the following properties at the root level of your workflow file:

```json
{
  "showOpenSavedProjectsButton": true,
  "showSaveProjectButton": true,
  "attributes": [],
  "vars": [],
  "widgets": [],
  "steps": []
}
```

|Property|Description|
|--|--|
|`showOpenSavedProjectsButton`|Displays a **My projects** button to open the list of saved projects.|
|`showSaveProjectButton`|Displays a **Save project** button to save the current project.|

![The top panel with the project buttons.](./images/ui-framework-project-buttons.png)

As an alternative, to bind these actions to custom buttons, define a [button](https://customerscanvas.com/docs/hub/admin-guide/workflows/ui-framework/widgets/button.md) widget and call the `auWizard` methods in its `onClick` event. For example, the following widget saves the current project:

```json
{
  "name": "save-project-button",
  "type": "button",
  "params": {
    "text": "Save project",
    "onClick": "{{ #function main.saveProject() }}"
  }
}
```

In the same way, `{{ #function main.openProjects() }}` opens the list of saved projects.

#### Filtering saved projects

To filter saved projects, use the following properties:

|Property|Description|
|--|--|
|`saveProjectData`|Stores a custom `data` object in a saved project.|
|`saveProjectFilterFunction`|Applies a filter function to the list of saved projects.|

:::note
The filter function receives an array of projects and must return an array, even if it is empty. Use the `.filter()` method of the array.
:::

The following configuration stores the selected design in a saved project and displays only projects with a specific design ID:

```json
{
  "vars": {
    "design": "{{ $['design-selector']?._ }}",
    "isVisible": true
  },
  "showOpenSavedProjectsButton": true,
  "showSaveProjectButton": true,
  "saveProjectData": "{{ vars.design }}",
  "saveProjectFilterFunction": "{{ #function(projects) projects.filter((p) => p.data.designId === '63ee085f1350128b0efd11ac') }}"
}
```

#### Requiring authentication for saving projects

To require temporary users to log in before saving projects, both of the following conditions must be met:

1. Set `authRequiredToSaveProject` to `true` in the workflow configuration.
2. Ensure the e-commerce system supports this functionality.

```json
{
  "showOpenSavedProjectsButton": true,
  "showSaveProjectButton": true,
  "authRequiredToSaveProject": true
}
```

For a complete example of a workflow with saved projects, refer to the [Three-step workflow](https://customerscanvas.com/docs/hub/admin-guide/workflows/ui-framework/tutorials/three-step-workflow.md) tutorial.
