---
title: Single- and Multi-Select Questions with Custom Item Template
product: Form Library
description: Make your single- or multi-select question stand out with custom choice options. Implement a custom component that renders item markup to style question items. Refer to this free demo for JavaScript to learn more.
framework: Vue 3
source: https://surveyjs.io/form-library/examples/add-custom-items-to-single-and-multi-select-questions/vue3js
index: https://surveyjs.io/form-library/examples/overview.md
---

# Single- and Multi-Select Questions with Custom Item Template (Vue 3)

SurveyJS Form Library supports the usage of custom templates to change the appearance of choice options in Checkboxes, Radio Button Group, and other single- or multi-select question types. This demo shows how to customize choice options using a custom template component. 

To create and apply a custom option template, follow the steps below:

1. Implement a component that renders option markup.          
    The component accepts the option configuration object as a prop. You can use its `value` and `text` properties to access the option's value to be saved in survey results and display text. 

2. Register the component so that it can be accessed by name.         
    In HTML/CSS/JavaScript projects, register the component in `ReactElementFactory` as shown in the `index.js` file.           
    In React, register the component in `ReactElementFactory` as shown in the `SurveyComponent.jsx` file.          
    In Angular, register the component in `AngularComponentFactory` as shown in the `custom-choice-item.component.ts` file.          
    In Vue.js, use [techniques native to this framework](https://vuejs.org/guide/components/registration).

3. Assign the component name to the question's [`itemComponent`](https://surveyjs.io/form-library/documentation/api-reference/questionselectbase#itemComponent) property in the survey JSON schema.

## Files

### `public/index.html`

```html
<div id="app" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; height:100%"></div>
```

### `src/CustomCheckboxItem.vue`

```html
<template>
    <div role="presentation" class="item-root">
        <label :class="getItemLabelClassName()" @mousedown="handleMouseDown">
            <input role="option"
                   :type="getOptionType()"
                   :class="'item-input'"
                   :name="props.question.name+props.item.value"
                   :id="props.question.getItemId(props.item)"
                   :checked="props.question.isItemSelected(props.item)"
                    @input="(e) => { change(e); }"
                    :value="props.item.value" />
            <span class="item-image">
                <svg class="item-svg">
                    <use :xlink:href="getIconId()"></use>
                </svg>
            </span>
            <sv-component :is="'survey-string'" :locString="props.item.locText" />
        </label>
    </div>
    <SvComponent
        v-if="item.isPanelShowing"
        :is="'survey-panel'"
        :element="item.panel"
        :cssClasses="question.cssClasses"
    />
    <SvComponent
        :is="'survey-other-choice'"
        v-if="item.isCommentShowing"
        :question="question"
        :item="item"
    />
</template>
<script setup lang="ts">
    import { ItemValue, Question } from "survey-core";
    import { SvComponent } from "survey-vue3-ui";

    const props = defineProps<{ question: Question; item: ItemValue }>();
    function getItemLabelClassName() {
        return ("item-label" + (props.question.isItemSelected(props.item) ? " item-label--selected" : ""));
    }
    function getIconId() {
        return props.question.isItemSelected(props.item) ? "#icon-plus" : "#icon-minus";
    }
    function getOptionType() {
        return "checkbox";
    }
    const change = (event: any) => {
        props.question.clickItemHandler(props.item, event.target.checked);
    };
    const handleMouseDown = () => {
        props.question.onMouseDown();
    };
</script>
```

### `src/CustomRadioItem.vue`

```html
<template>
    <div role="presentation" class="item-root">
        <label :class="getItemLabelClassName()" @mousedown="handleMouseDown">
            <input role="option"
                   :type="getOptionType()"
                   :class="'item-input'"
                   :name="props.question.name+props.item.value"
                   :id="props.question.getItemId(props.item)"
                   :checked="props.question.isItemSelected(props.item)"
                    @input="(e) => { change(e); }"
                    :value="props.item.value" />
            <span class="item-image">
                <svg class="item-svg">
                    <use :xlink:href="getIconId()"></use>
                </svg>
            </span>
            <sv-component :is="'survey-string'" :locString="props.item.locText" />
        </label>
    </div>
    <SvComponent
        v-if="item.isPanelShowing"
        :is="'survey-panel'"
        :element="item.panel"
        :cssClasses="question.cssClasses"
    />
    <SvComponent
        :is="'survey-other-choice'"
        v-if="item.isCommentShowing"
        :question="question"
        :item="item"
    />
</template>
<script setup lang="ts">
    import { ItemValue, Question } from "survey-core";
    import { SvComponent } from "survey-vue3-ui";

    const props = defineProps<{ question: Question; item: ItemValue }>();
    function getItemLabelClassName() {
        return ("item-label" + (props.question.isItemSelected(props.item) ? " item-label--selected" : ""));
    }
    function getIconId() {
        return props.question.isItemSelected(props.item) ? "#icon-plus" : "#icon-minus";
    }
    function getOptionType() {
        return "radio";
    }
    const change = (event: any) => {
        props.question.clickItemHandler(props.item, event.target.checked);
    };
    const handleMouseDown = () => {
        props.question.onMouseDown();
    };
</script>
```

### `src/App.vue`

```html
<template>
    <SurveyComponent :model="survey" />
</template>
<script setup lang="ts">
    import { Model } from "survey-core";
    import { SurveyComponent } from "survey-vue3-ui";
    import "survey-core/survey-core.min.css";
    import "./index.css";
    import { json } from "./json";

    const survey = new Model(json);
    survey.onComplete.add((sender, options) => {
        console.log(JSON.stringify(sender.data, null, 3));
    });
</script>
```

### `src/index.css`

```css
.item-input {
    display: none;
}

.item-root {
    margin-bottom: 8px;
}

.item-svg {
    padding: 4px;
    width: 32px;
    height: 32px;
}

.item-image {
    display: flex;
    flex-direction: row;
    align-items: center;
}

.item-text {
    height: 24px;
    font-family: 'Open Sans';
    font-style: normal;
    font-weight: 400;
    font-size: 16px;
    line-height: 24px;
    color: #161616;
    user-select: none;
}

.item-label {
    box-sizing: border-box;
    display: flex;
    flex-direction: row;
    align-items: center;
    padding: 4px 32px 4px 16px;
    gap: 12px;
    height: 48px;
    width: fit-content;
    background: rgba(255, 152, 20, 0.1);
    border: 1px solid #ff9814;
    border-radius: 100px;
    margin-bottom: 12px;
}

.item-label.item-label--selected {
    background: rgba(25, 179, 148, 0.1);
    border: 1px solid #19b394;
    border-radius: 100px;
}

.question-note {
    white-space: initial;
}
```

### `src/json.ts`

```ts
export const json = {
  "elements": [
    {
      "type": "radiogroup",
      "name": "singleselect",
      "title": "Single-select question with a custom item template",
      "itemComponent": "custom-radio-item",
      "showOtherItem": true,
      "choices": [ "Item 1", "Item 2", "Item 3" ]
    },
    {
      "type": "checkbox",
      "name": "multiselect",
      "title": "Multi-select question with a custom item template",
      "itemComponent": "custom-checkbox-item",
      "showOtherItem": true,
      "showSelectAllItem": true,
      "choices": [ "Item 1", "Item 2", "Item 3" ]
    }
  ]
};
```

### `src/main.ts`

```ts
import { createApp } from "vue";
import App from "./App.vue";
import CustomRadioItem from "./CustomRadioItem.vue";
import CustomCheckboxItem from "./CustomCheckboxItem.vue";
import { SvgRegistry } from "survey-core";

const app = createApp(App);
app.component("custom-radio-item", CustomRadioItem);
app.component("custom-checkbox-item", CustomCheckboxItem);

const svgPlus = "<svg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M30 16H17V3H16V16H3V17H16V30H17V17H30V16Z' fill='#19B394'/></svg>";
const svgMinus = "<svg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M30 16H3V17H30V16Z' fill='#FF9814'/></svg>";
SvgRegistry.registerIcon("icon-plus", svgPlus);
SvgRegistry.registerIcon("icon-minus", svgMinus);
app.mount("#app");
```

### `src/shims-vue.d.ts`

```ts
/* eslint-disable */
declare module "*.vue" {
    import type { DefineComponent } from "vue"
    const component: DefineComponent<{}, {}, any>
    export default component
}
```

### `.eslintrc.js`

```js
module.exports = {
    root: true,
    env: {
        node: true
    },
    extends: [
        "plugin:vue/vue3-essential",
        "eslint:recommended",
        "@vue/typescript/recommended",
        "@vue/prettier",
        "@vue/prettier/@typescript-eslint"
    ],
    parserOptions: {
        ecmaVersion: 2020
    },
    rules: {
        "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
        "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off"
    },
    overrides: [
        {
            files: [
                "**/__tests__/*.{j,t}s?(x)",
                "**/tests/unit/**/*.spec.{j,t}s?(x)"
            ],
            env: {
                jest: true
            }
        }
    ]
};
```

### `babel.config.js`

```js
module.exports = {
  presets: ["@vue/cli-plugin-babel/preset"]
};
```

### `package.json`

```json
{
  "name": "surveyjs-library-vue3",
  "version": "0.1.0",
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "tslib": "2.6.1",
    "vue": "^3.4.1",
    "survey-core": "latest",
    "survey-vue3-ui": "latest",
    "vue-router": "^4.0.0-0",
    "vuex": "^4.0.0-0"
  },
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "^2.33.0",
    "@typescript-eslint/parser": "^2.33.0",
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-pwa": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-typescript": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "@vue/compiler-sfc": "^3.0.0",
    "@vue/eslint-config-prettier": "^6.0.0",
    "@vue/eslint-config-typescript": "^5.0.2",
    "@vue/test-utils": "^2.0.0-0",
    "eslint": "^6.7.2",
    "eslint-plugin-prettier": "^3.1.3",
    "eslint-plugin-vue": "^7.0.0-0",
    "node-sass": "^4.12.0",
    "prettier": "^1.19.1",
    "sass-loader": "^8.0.2",
    "typescript": "~3.9.3"
  }
}
```

### `tsconfig.json`

```json
{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "sourceMap": true,
    "baseUrl": ".",
    "types": [
      "webpack-env",
      "jest"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}
```

## Other Frameworks

- [Angular](https://surveyjs.io/form-library/examples/add-custom-items-to-single-and-multi-select-questions/angular.md)
- [React](https://surveyjs.io/form-library/examples/add-custom-items-to-single-and-multi-select-questions/reactjs.md)
- [Vanilla JS](https://surveyjs.io/form-library/examples/add-custom-items-to-single-and-multi-select-questions/vanillajs.md)
