---
title: Add Custom Fonts
product: Survey Creator
description: Theme Editor is a form styling tool integrated into SurveyJS Form Builder. With the Theme Editor, form creators can use a set of UI controls to adjust form colors, fonts, sizes, and more. If a desired font is missing from the predefined font collection, you can easily register a custom font and make it available in the font families list. Try out a live demo for JavaScript with a step-by-step guide on how to add and use custom fonts in SurveyJS Form Builder.
framework: Vue 3
source: https://surveyjs.io/survey-creator/examples/add-custom-fonts/vue3js
index: https://surveyjs.io/survey-creator/examples/overview.md
---

# Add Custom Fonts (Vue 3)

Theme Editor in SurveyJS Form Builder enables designers to personalize surveys and forms by creating custom UI themes. Although Theme Editor ships with a set of default fonts, you might want to use custom branded fonts to enhance the look and feel of your forms and give your surveys a personalized touch. To add custom fonts to Theme Editor, follow these steps:

1. Register custom fonts.      
Add custom fonts to the page with Survey Creator. In this demo, custom fonts are loaded from a server and added as a `<style>` element to the `<head>` of the page. Refer to the `loadFont` function for code listing. The following code shows how you can specify custom TTF fonts using CSS:

    ```css
    @font-face {
      font-family: "RobotoMono-Regular"; /* Provide a name for your font */
      src: url("./custom-fonts/RobotoMono-Regular.ttf") format("truetype");
      /* Add other font properties, like `font-weight` and `font-style` if needed */
    }
    @font-face {
      font-family: "RobotoMono-Italic"; /* Provide a name for your font */
      src: url("./custom-fonts/RobotoMono-Italic.ttf") format("truetype");
      /* Add other font properties, like `font-weight` and `font-style` if needed */
    }
    ```

1. Add custom fonts to the font list.      
Import the `DefaultFonts` array from the `"survey-creator-core"` module and push new font names to this array. The font names must begin with a font's `font-family` attribute value:

    ```js
    import { DefaultFonts } from "survey-creator-core";

    DefaultFonts.push(
        "RobotoMono-Regular, monospace",
        "RobotoMono-Italic, monospace"
    );
    ```

Now, custom fonts are available within Theme Editor. For instance, you can apply them using the **Font Family** setting in the **Appearance** tab:

<img src="/Content/Images/examples/custom-fonts/custom-fonts.png" alt="Access custom fonts in SurveyJS Theme Editor" width="1600" height="782">

## Files

### `public/index.html`

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

### `src/survey_json.js`

```js
export const formJSON = {
  "title": "Online Check-in",
  "description": "Check-in is available 2 to 24 hours prior to departure for all destinations. To complete the check-in process, please fill out the form below.",
  "completedHtml": "<div style=\"max-width:640px;text-align:center;margin:16px auto;\">\n\n<div style=\"padding:0 24px;\">\n<h4>Check-in complete.</h4>\n<p>Thank you for checking in. Your journey with us is all set. Have a great flight.</p>\n</div>\n\n</div>\n",
  "pages": [
    {
      "name": "page1",
      "elements": [
        {
          "type": "paneldynamic",
          "name": "passengers",
          "width": "100%",
          "minWidth": "256px",
          "titleLocation": "hidden",
          "templateElements": [
            {
              "type": "text",
              "name": "first-name",
              "width": "35%",
              "minWidth": "208px",
              "title": "PASSENGER #{panelIndex} INFO",
              "titleLocation": "top",
              "placeholder": "First name"
            },
            {
              "type": "text",
              "name": "last-name",
              "width": "30%",
              "minWidth": "172px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "placeholder": "Last name"
            },
            {
              "type": "dropdown",
              "name": "prefix",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "choices": [
                "Mr.",
                "Mrs.",
                "Ms."
              ],
              "choicesOrder": "random",
              "placeholder": "Prefix",
              "allowClear": false
            },
            {
              "type": "multipletext",
              "name": "birthdate",
              "width": "65%",
              "minWidth": "256px",
              "items": [
                {
                  "name": "date",
                  "inputType": "date",
                  "title": "Date of birth"
                }
              ]
            },
            {
              "type": "text",
              "name": "nationality",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "placeholder": "Nationality"
            },
            {
              "type": "text",
              "name": "passport-number",
              "width": "100%",
              "minWidth": "256px",
              "title": "PASSENGER #{panelIndex} ID",
              "titleLocation": "top",
              "placeholder": "Passport #"
            },
            {
              "type": "dropdown",
              "name": "passport-issue-country",
              "width": "35%",
              "minWidth": "208px",
              "choicesByUrl": {
                "url": "https://surveyjs.io/api/CountriesExample",
                "valueName": "name"
              },
              "placeholder": "Country of issue",
              "allowClear": false
            },
            {
              "type": "multipletext",
              "name": "passport-exp-date",
              "width": "65%",
              "minWidth": "256px",
              "startWithNewLine": false,
              "items": [
                {
                  "name": "date",
                  "inputType": "date",
                  "title": "Exp. date"
                }
              ]
            }
          ],
          "panelCount": 1,
          "minPanelCount": 1,
          "confirmDeleteText": "Do you want to delete the passenger?",
          "addPanelText": "ADD PASSENGER",
          "removePanelText": "REMOVE",
          "showProgressBar": false
        },
        {
          "type": "panel",
          "name": "person-to-notify",
          "elements": [
            {
              "type": "text",
              "name": "person-to-notify-first-name",
              "width": "35%",
              "minWidth": "208px",
              "title": "PERSON TO NOTIFY",
              "titleLocation": "top",
              "setValueIf": "{passengers[0].first-name} notempty",
              "setValueExpression": "{passengers[0].first-name}",
              "placeholder": "First name"
            },
            {
              "type": "text",
              "name": "person-to-notify-last-name",
              "width": "30%",
              "minWidth": "172px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "setValueIf": "{passengers[0].last-name} notempty",
              "setValueExpression": "{passengers[0].last-name}",
              "placeholder": "Last name"
            },
            {
              "type": "dropdown",
              "name": "person-to-notify-prefix",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "setValueIf": "{passengers[0].prefix} notempty",
              "setValueExpression": "{passengers[0].prefix}",
              "choices": [
                "Mr.",
                "Mrs.",
                "Ms."
              ],
              "choicesOrder": "random",
              "placeholder": "Prefix",
              "allowClear": false
            },
            {
              "type": "text",
              "name": "person-to-notify-email",
              "width": "65%",
              "minWidth": "256px",
              "inputType": "email",
              "placeholder": "Email"
            },
            {
              "type": "text",
              "name": "person-to-notify-phone",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "placeholder": "Phone"
            },
            {
              "type": "text",
              "name": "person-to-notify-address",
              "width": "100%",
              "minWidth": "256px",
              "title": "ADDRESS",
              "titleLocation": "top",
              "placeholder": "Address line 1"
            },
            {
              "type": "text",
              "name": "person-to-notify-city",
              "width": "35%",
              "minWidth": "208px",
              "placeholder": "City"
            },
            {
              "type": "text",
              "name": "person-to-notify-state",
              "width": "30%",
              "minWidth": "172px",
              "startWithNewLine": false,
              "placeholder": "State"
            },
            {
              "type": "text",
              "name": "person-to-notify-zip",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "placeholder": "Zip Code"
            },
            {
              "type": "dropdown",
              "name": "person-to-notify-country",
              "width": "100%",
              "minWidth": "256px",
              "choicesByUrl": {
                "url": "https://surveyjs.io/api/CountriesExample",
                "valueName": "name"
              },
              "placeholder": "Country",
              "allowClear": false
            }
          ],
          "questionTitleLocation": "hidden",
          "width": "100%",
          "minWidth": "256px"
        },
        {
          "type": "panel",
          "name": "flight",
          "elements": [
            {
              "type": "text",
              "name": "departure-booking-number",
              "width": "65%",
              "minWidth": "256px",
              "title": "DEPARTURE",
              "titleLocation": "top",
              "validators": [
                {
                  "type": "regex",
                  "text": "Your booking number must consist of exactly 6 digits.",
                  "regex": "^\\d{6}$"
                }
              ],
              "maxLength": 6,
              "placeholder": "Please enter your 6-digit booking number"
            },
            {
              "type": "text",
              "name": "departure-flight-number",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "placeholder": "Flight #"
            },
            {
              "type": "multipletext",
              "name": "departure-date",
              "width": "65%",
              "minWidth": "256px",
              "items": [
                {
                  "name": "date",
                  "inputType": "date",
                  "title": "Date"
                }
              ]
            },
            {
              "type": "multipletext",
              "name": "departure-time",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "items": [
                {
                  "name": "time",
                  "inputType": "time",
                  "title": "Time"
                }
              ]
            },
            {
              "type": "dropdown",
              "name": "departure-country",
              "width": "65%",
              "minWidth": "256px",
              "title": "DEPARTING FROM",
              "choicesByUrl": {
                "url": "https://surveyjs.io/api/CountriesExample"
              },
              "placeholder": "Country",
              "allowClear": false
            },
            {
              "type": "text",
              "name": "departure-city",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "placeholder": "City"
            },
            {
              "type": "dropdown",
              "name": "destination-country",
              "width": "65%",
              "minWidth": "256px",
              "title": "DESTINATION",
              "titleLocation": "top",
              "choicesByUrl": {
                "url": "https://surveyjs.io/api/CountriesExample"
              },
              "placeholder": "Country",
              "allowClear": false
            },
            {
              "type": "text",
              "name": "destination-city",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "title": " ",
              "titleLocation": "top",
              "placeholder": "City"
            },
            {
              "type": "checkbox",
              "name": "connecting-flight",
              "width": "100%",
              "minWidth": "256px",
              "choices": [
                {
                  "value": "true",
                  "text": "I have a connecting flight"
                }
              ]
            },
            {
              "type": "multipletext",
              "name": "connecting-flight-date",
              "visibleIf": "{connecting-flight} = ['true']",
              "width": "65%",
              "minWidth": "256px",
              "items": [
                {
                  "name": "date",
                  "inputType": "date",
                  "title": "Date"
                }
              ]
            },
            {
              "type": "multipletext",
              "name": "connecting-flight-time",
              "visibleIf": "{connecting-flight} = ['true']",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "items": [
                {
                  "name": "time",
                  "inputType": "time",
                  "title": "Time"
                }
              ]
            },
            {
              "type": "text",
              "name": "connecting-flight-booking-number",
              "visibleIf": "{connecting-flight} = ['true']",
              "width": "65%",
              "minWidth": "256px",
              "title": "DEPARTURE",
              "validators": [
                {
                  "type": "regex",
                  "text": "Your booking number must consist of exactly 6 digits.",
                  "regex": "^\\d{6}$"
                }
              ],
              "maxLength": 6,
              "placeholder": "Please enter your 6-digit booking number"
            },
            {
              "type": "text",
              "name": "connecting-flight-number",
              "visibleIf": "{connecting-flight} = ['true']",
              "width": "35%",
              "minWidth": "208px",
              "startWithNewLine": false,
              "placeholder": "Flight #"
            }
          ],
          "questionTitleLocation": "hidden",
          "width": "100%",
          "minWidth": "256px"
        }
      ]
    }
  ],
  "questionDescriptionLocation": "underInput",
  "questionErrorLocation": "bottom",
  "completeText": "Check In",
  "widthMode": "static",
  "width": "860"
}
```

### `src/theme_json.js`

```js
export const customTheme = {
    "backgroundImageFit": "cover",
    "backgroundImageAttachment": "scroll",
    "backgroundOpacity": 1,
    "cssVariables": {
        "--sjs2-color-bg-basic-primary": "rgba(255, 255, 255, 1)",
        "--sjs2-color-utility-property-grid": "rgba(255, 255, 255, 1)",
        "--sjs2-color-utility-tabs": "rgba(255, 255, 255, 1)",
        "--sjs2-color-utility-toolbox": "rgba(255, 255, 255, 1)",
        "--sjs2-color-bg-basic-primary-dim": "rgba(248, 248, 248, 1)",
        "--sjs2-color-bg-neutral-tertiary-dim": "rgba(243, 243, 243, 1)",
        "--sjs2-color-utility-surface-survey-panelless": "rgba(243, 243, 243, 1)",
        "--sjs2-color-utility-surface-survey": "rgba(243, 243, 243, 1)",
        "--sjs2-color-bg-basic-secondary": "rgba(249, 249, 249, 1)",
        "--sjs2-color-bg-basic-secondary-dim": "rgba(243, 243, 243, 1)",
        "--sjs2-color-fg-basic-primary": "rgba(0, 0, 0, 0.91)",
        "--sjs2-color-fg-basic-secondary": "rgba(0, 0, 0, 0.45)",
        "--sjs2-color-project-brand-600": "rgba(25, 179, 148, 1)",
        "--sjs2-color-bg-brand-secondary": "rgba(25, 179, 148, 0.1)",
        "--sjs2-color-bg-brand-primary-dim": "rgba(20, 164, 139, 1)",
        "--sjs2-color-fg-brand-on-primary": "rgba(255, 255, 255, 1)",
        "--sjs2-color-fg-brand-primary-disabled": "rgba(255, 255, 255, 0.25)",
        "--sjs2-base-unit-size": "8px",
        "--sjs2-base-unit-spacing": "8px",
        "--sjs2-base-unit-radius": "4px",
        "--sjs2-color-bg-accent-primary": "rgba(255, 152, 20, 1)",
        "--sjs2-color-bg-accent-secondary": "rgba(255, 152, 20, 0.1)",
        "--sjs2-color-bg-accent-secondary-dim": "rgba(255, 152, 20, 0.25)",
        "--sjs2-color-fg-accent-on-primary": "rgba(255, 255, 255, 1)",
        "--sjs2-color-fg-accent-primary-disabled": "rgba(255, 255, 255, 0.25)",
        "--sjs2-border-effect-surface-default": "0px 1px 2px 0px rgba(0, 0, 0, 0.15)",
        "--sjs2-border-effect-floating-default": "0px 2px 6px 0px rgba(0, 0, 0, 0.1),0px 8px 16px 0px rgba(0, 0, 0, 0.1)",
        "--sjs2-border-effect-component-formbox-default": "inset 0px 1px 2px 0px rgba(0, 0, 0, 0.15)",
        "--sjs2-border-effect-component-check-true-default": "inset 0px 1px 2px 0px rgba(0, 0, 0, 0.15)",
        "--sjs2-border-effect-component-check-false-default": "inset 0px 1px 2px 0px rgba(0, 0, 0, 0.15)",
        "--sjs2-color-border-basic-secondary": "rgba(0, 0, 0, 0.09)",
        "--sjs2-color-component-input-default-line": "rgba(0, 0, 0, 0.16)",
        "--sjs2-color-border-basic-secondary-overlay": "rgba(0, 0, 0, 0.16)",
        "--sjs2-color-bg-alert-primary": "rgba(229, 10, 62, 1)",
        "--sjs2-color-bg-alert-secondary": "rgba(229, 10, 62, 0.1)",
        "--sjs2-color-fg-alert-on-primary": "rgba(255, 255, 255, 1)",
        "--sjs2-color-bg-positive-primary": "rgba(25, 179, 148, 1)",
        "--sjs2-color-bg-positive-secondary": "rgba(25, 179, 148, 0.1)",
        "--sjs2-color-fg-positive-on-primary": "rgba(255, 255, 255, 1)",
        "--sjs2-color-bg-note-primary": "rgba(67, 127, 217, 1)",
        "--sjs2-color-bg-note-secondary": "rgba(67, 127, 217, 0.1)",
        "--sjs2-color-fg-note-on-primary": "rgba(255, 255, 255, 1)",
        "--sjs2-color-bg-warning-primary": "rgba(255, 152, 20, 1)",
        "--sjs2-color-bg-warning-secondary": "rgba(255, 152, 20, 0.1)",
        "--sjs2-color-fg-warning-on-primary": "rgba(255, 255, 255, 1)",
        "--sjs2-typography-font-family-text": "RobotoMono-Regular, monospace",
        "--sjs2-typography-font-weight-component-header-title": "700",
        "--sjs2-typography-font-size-component-header-description": "20px",
        "--sjs2-typography-font-weight-component-page-title": "700"
    },
    "themeName": "default",
    "colorPalette": "light",
    "isPanelless": false
};
```

### `src/App.vue`

```html
<template>
    <SurveyCreatorComponent :model="creator" />
</template>
<script setup lang="ts">
    import { SurveyCreatorModel } from "survey-creator-core";
    import { SurveyCreatorComponent } from "survey-creator-vue";
    import { formJSON } from "./survey_json";
    import { customTheme } from "./theme_json";
    import { DefaultFonts } from "survey-creator-core";
    
    import "survey-core/survey.i18n";
    import "survey-creator-core/survey-creator-core.i18n";
    import "survey-core/survey-core.css";
    import "survey-creator-core/survey-creator-core.css";
    import "./index.css";
    import SurveyTheme from "survey-core/themes";
    import { registerCreatorTheme, registerSurveyTheme } from "survey-creator-core";

    registerSurveyTheme(SurveyTheme); // Add predefined Form Library UI themes
    registerCreatorTheme(SurveyTheme); // Add predefined Survey Creator UI themes

    let fontIsLoading = true;
    let serverHost = window.location.origin;
    if (serverHost.indexOf("localhost") < 0 && serverHost.indexOf("surveyjs") < 0)
        serverHost = "https://surveyjs.io";
    
    loadFont({
        serverHost: serverHost,
        fontPath: "fontbase64_robotomonoregular",
        fontFamily: "RobotoMono-Regular"
    });
    
    loadFont({
        serverHost: serverHost,
        fontPath: "fontbase64_robotomonoitalic",
        fontFamily: "RobotoMono-Italic"
    });
    
    function loadFont({ serverHost, fontPath, fontFamily }) { 
        const fontUrl = serverHost + "/api/" + fontPath;
        fetch(fontUrl)
            .then(response => {
                if (!response.ok) {
                    throw new Error(`HTTP error: ${response.status}`);
                }
                return response.text();
            })
            .then(base64Font => {
                // Create a `<style>` element for the loaded font
                var style = document.createElement('style');
    
                // Set the `innerHTML` aatribute of the `<style>` element
                // to the `@font-face` rule with base64-encoded font data
                style.innerHTML = "@font-face { font-family: '" + fontFamily + "'; src: url(data:application/x-font-woff;charset=utf-8;base64," + base64Font + ") format('truetype'); }";
    
                // Append the `<style>` element to the document `<head>`
                document.head.appendChild(style);
                fontIsLoading = false;
            });
    };
    
    const creator = new SurveyCreatorModel({ showThemeTab: true });
    DefaultFonts.push(
        "RobotoMono-Regular, monospace",
        "RobotoMono-Italic, monospace"
    );
    
    creator.JSON = formJSON;
    creator.theme = customTheme;
    creator.activeTab = "theme";
</script>
```

### `src/index.css`

```css
/* You can add your custom CSS here. */
```

### `src/main.ts`

```ts
import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);
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",
    "survey-creator-core": "latest",
    "survey-creator-vue": "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/survey-creator/examples/add-custom-fonts/angular.md)
- [React](https://surveyjs.io/survey-creator/examples/add-custom-fonts/reactjs.md)
- [jQuery](https://surveyjs.io/survey-creator/examples/add-custom-fonts/jquery.md)
- [Vanilla JS](https://surveyjs.io/survey-creator/examples/add-custom-fonts/vanillajs.md)
