arrow-left

Only this pageAll pages
gitbookPowered by GitBook
1 of 68

17.latest (LTS)

Loading...

Loading...

Loading...

Installation

Loading...

Loading...

Upgrading

Loading...

Loading...

Loading...

Editor

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Developer

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Tutorials

Loading...

Loading...

Loading...

Umbraco Forms Documentation

Documentation on how to work with Umbraco Forms for both editors and developers.

Umbraco Forms is a tool that lets you build forms of all shapes and sizes and put them on your Umbraco websites. Build forms using a long list of elements like multiple choice, dropdowns, text areas and checkboxes. Choose between a series of different workflows and control what happens once a form has been submitted.

Purchase Umbraco Formsarrow-up-right or sign up for an Umbraco Cloudarrow-up-right project where Umbraco Forms is part of the package.

hashtag
Quick Links

Upgrading Umbraco Formschevron-rightAttaching Workflowschevron-rightUmbraco Forms in the Databasechevron-right

Legacy Documentation

This documentation platform covers only major versions of Umbraco Forms. If you are using an older version of Umbraco Forms, you will need to go elsewhere.

Umbraco 10 and above Documentation

Umbraco 8 Documentation

Installing Umbraco Forms

Install Umbraco Forms in a few steps.

Creating a Form

Create new Forms and add them to your Umbraco site in minutes.

Preparing your Frontend

Ensure you have the necessary client dependencies before adding a Form to your site.

Cover
Cover
Cover

Version Specific Upgrade Notes

Version specific documentation for upgrading to new major versions of Umbraco Forms.

This article provides specific upgrade documentation for migrating to Umbraco Forms version 17.

circle-info

If you are upgrading to a minor or patch version, you can find the details about the changes in the Release Notes article.

hashtag
Version Specific Upgrade Notes History

Version 17 of Umbraco Forms has a minimum dependency on Umbraco CMS core of 17.0.0. It runs on .NET 9.

hashtag
Legacy version specific upgrade notes

You can find the version specific upgrade notes for versions out of support in the .

Date

The date picker uses a front-end library called Pikadayarrow-up-right to display a UI to pick dates.

Date picker on frontend

Pikaday date picker can be localized based on the page the Form is rendered on.

The date picker displays the picked date in the required locale. Using JavaScript, a hidden field is updated with a standard date format to send to the server for storing record submissions. This avoids the locale mixing up the dates.

To achieve localized date, a Razor partial view is included at /Views/Partials/Forms/Themes/default/DatePicker.cshtml.

The DatePicker.cshtml includes the moment-with-locales.min.js library to help with the date locale formatting and the appropriate changes to Pikaday to support the locales. If you wish to use a different DatePicker component, edit the DatePicker.cshtml file as per your needs.

hashtag
Configure the date picker

The Date picker has to control the number of years shown in the picker and the date format.

Upgrading Umbraco Forms

This article shows how to manually upgrade Umbraco Forms to run the latest version.

When upgrading Umbraco Forms, be sure to also consult the to learn about potential breaking changes and common pitfalls.

hashtag
Get the latest version of Umbraco Forms

To get the latest version of Umbraco Forms, you can upgrade using:

File Upload

The File Upload field allows the users to upload a file along with the Form on your website.

In this article, you will find details about the configuration options you have for the File Upload field.

hashtag
Predefined allowed File Types

You can choose to specify which files you want to allow the user to upload, when accessing the Form.

reCAPTCHA Enterprise

In Umbraco Forms, reCAPTCHA Enterprise comes out of the box.

reCAPTCHA Enterprise allows you to verify if an interaction is legitimate without any user interaction.

hashtag
Enabling reCAPTCHA Enterprise

Follow these steps to enable reCAPTCHA Enterprise in Umbraco Forms:

reCAPTCHA V3

In Umbraco Forms, reCAPTCHA V3 comes out of the box.

reCAPTCHA v3 allows you to verify if an interaction is legitimate without any user interaction.

hashtag
Enabling reCAPTCHA V3

Follow these steps to enable reCAPTCHA V3 in Umbraco Forms:

Installing Umbraco Forms

This article covers two ways to install Umbraco Forms.

Install Umbraco Forms following either of the two guides:

  • , or

  • .

reCAPTCHA V2

In Umbraco Forms, reCAPTCHA V2 comes out of the box to help you to protect your site from spam, malicious people, and so on.

hashtag
Enabling reCAPTCHA V2

Follow these steps to enable reCAPTCHA V2 in Umbraco Forms:

Excluding a built-in field

Umbraco Forms comes with some built-in fields however it is possible to exclude/remove them if necessary. There might some use cases where you have no use for file upload and don't want editors using them. Or perhaps you want to remove a field to replace it with one with enhanced functionality that you build yourself.

hashtag
Example

The following class shows how to exclude built-in field types using a custom composer. The Password, Recaptcha2 and RichText

Webhooks

Umbraco Forms will register events for workflow operations that you can use with .

Workflows are operations that you can associate with form submission, approval, or rejection actions. You can use these where you need to notify external systems of the success or failure of a workflow.

On the Umbraco Settings > Advanced > Webhooks dashboard, you can configure webhooks to respond to workflows.

You can amend the registration of workflow events in code.

To remove the webhooks that are added by default you can use a composer as follows:

Overview

In this section, you can find a set of different tutorials to use when creating and working with Umbraco Forms.

hashtag
Tutorials

hashtag

Legacy documentation on GitHubarrow-up-right

Learn how to create a Contact Form and add it to your website.

Creating a Contact Form
To allow only specific files:
  1. Select the specific File Types the user should be able to upload.

  2. Click Submit.

circle-info

We recommend selecting only specified files, to limit malicious code to be uploaded, whenever the user is submitting the Form.

hashtag
User Defined Allowed File Types

If the list of predefined file types does not include a specific file type, you can add additional ones.

To add new file type:

  1. Type a file extension name in the User defined allowed file types field.

  2. Click +.

  3. Click Submit.

hashtag
Server-side file validation

The file upload field type will verify the file contents using the registered set of IFileStreamSecurityValidator instances.

To read more about this feature, see Server-side file validationarrow-up-right in the CMS documentation.

fileupload
configuration settings
using Umbraco.Cms.Core.Composing;
using Umbraco.Forms.Core.Extensions;

internal sealed class TestComposer : IComposer
{
    public void Compose(IUmbracoBuilder builder)
        => builder.WebhookEvents().AddForms(formsBuilder => formsBuilder.RemoveDefault());
}
Umbraco webhooksarrow-up-right
Webhook events

Go to the Forms section in the backoffice.

  • Find the form that should have ReCAPTCHA Enterprise enabled.

  • Add a new question and select ReCAPTCHA Enterprise with Score as its answer type.

  • Make sure the field is set as Mandatory.

  • Configure reCAPTCHA settings in the appSettings.json file to include SiteKey, ApiKey and ProjectId:

  • You can create your keys by logging into your reCAPTCHA accountarrow-up-right.

    "Umbraco": {
        "Forms": {
            "FieldTypes": {
                "RecaptchaEnterprise": {
                    "SiteKey": "",
                    "ApiKey": "",
                    "ProjectId": ""
                }
            }
        }
    }

    Go to the Forms section in the backoffice.

  • Find the form that should have ReCAPTCHA v3 enabled.

  • Add a new question and select ReCAPTCHA v3 with Score as its answer type.

  • Make sure the field is set as Mandatory.

  • Configure ReCAPTCHA settings in the appSettings.json file to include public and private keys:

  • You can create your keys by logging into your reCAPTCHA accountarrow-up-right.

    hashtag
    Additional Configuration Options

    Refer to Configuration to see additional options that can be set in the appSettings.json for reCAPTCHA v3.

    Go to the Forms section in the backoffice.
  • Find the form that should have ReCAPTCHA v2 enabled.

  • Add a new question and select ReCAPTCHA v2 as its answer type.

  • Make sure the field is set as Mandatory.

  • Configure ReCAPTCHA settings in the appSettings.json file to include public and private keys:

  • You can create your keys by logging into your reCAPTCHA accountarrow-up-right.

    "Umbraco"{
        "Forms": {
          "FieldTypes": {
            "Recaptcha2": {
                "PublicKey": "",
                "PrivateKey": ""
              }
            }
          }
       }
    field types (or "answers") will no longer be available for selection when creating a form in the backoffice.
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Forms.Core.Providers.Extensions;
    using Umbraco.Forms.Core.Providers.FieldTypes;
    
    namespace MyNamespace
    {
        public class MyFormFieldsComposer : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.FormsFields()
                  .Exclude<Password>()
                  .Exclude<Recaptcha2>()
                  .Exclude<RichText>();
            }
        }
    }
    "Umbraco"{
        "Forms": {
          "FieldTypes": {
            "Recaptcha3": {
                "SiteKey": "",
                "PrivateKey": ""
              }
            }
          }
       }

    NuGet

  • Visual Studio

  • hashtag
    NuGet

    • NuGet installs the latest version of the package when you use the dotnet add package Umbraco.Forms command unless you specify a package version: dotnet add package Umbraco.Forms --version <VERSION>

    • After you have added a package reference to your project by executing the dotnet add package Umbraco.Forms command in the directory that contains your project file, run dotnet restore to install the package.

    hashtag
    Visual Studio

    1. Go to Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution... in Visual Studio, to upgrade your Forms:

    2. Select Umbraco.Forms.

    3. Select the latest version from the Version drop-down and click Install.

    1. When the command completes, open the .csproj file to make sure the package reference is updated:

    version specific upgrade notes
    hashtag
    Prerequisites

    An Umbraco CMS website set up locally or in a development environment.

    hashtag
    Installation via NuGet

    To install Umbraco Forms using NuGet in Visual Studio, follow these steps:

    1. Open the project in Visual Studio.

    2. Go to Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution.

    3. Select the Browse tab.

    4. Search for Umbraco.Forms.

    5. Select the package.

    6. Choose the project you want to install it into.

    7. Install the latest stable version of the package.

    hashtag
    Installing using the terminal

    You can also install Umbraco Forms using a terminal. Follow these steps:

    1. Open a terminal of your choice.

    2. Navigate to the folder containing your Umbraco CMS project.

    3. Install the Umbraco.Forms NuGet package with the following command:

    1. Build and run your project.

    hashtag
    Start Building Forms

    Once the installation is complete and the site is running, you will see a Forms section in the Umbraco backoffice similar to the screen below:

    Create form

    The next step is to configure the license.

    hashtag
    Using Forms

    For details on creating and managing forms, see the Editor Documentation.

    Via NuGet
    Via a Terminal

    Viewing And Exporting Entries

    To view the Entries for each Form, go to the Form and click on the Entries tab.

    Tree

    hashtag
    Video overview

    hashtag
    Entries Overview

    When accessing the Entries viewer, you will be able to see all the entries submitted via the Form.

    hashtag
    Viewing the Entries

    By default, the list is filtered to show entries only from the past month. If you want to change the date range, pick the appropriate time period from the date picker. You can also filter the entries by specific words using the Search field on the left.

    Click Entry details on each record in the list to open the full set of information recorded for the form entry. Clicking on the entry record displays the Clear and Delete buttons.

    hashtag
    Editing the Entries

    If configured via the permissions model and supported by the version of Umbraco Forms you are running, entries may be editable via the backoffice. If available, click the Edit button to switch the read-only view of an entry to an editable one and Save to record the changes. An audit trail will show who and when updates on the entry were made.

    Validation will operate as is configured for the form in terms of mandatory fields and those that must match a particular pattern. Conditional display of fields is not supported.

    hashtag
    Exporting Entries

    To export all the entries from your Form:

    1. Go to the Forms section.

    2. Navigate to the Form Entries you wish to export.

    3. Click Export.

    If you have fields that allow the user to upload files within your form, you will also have the option to download a zip file containing these files. You can either download the files in the structure that they are stored on the web server's disk. Or you can download them organised by entry, so it's easier to match up the entry listed in the spreadsheet download with the uploaded file(s).

    hashtag
    Analytics

    For a deeper understanding of form submission trends, workflow performance, and submission origins, see the section. Analytics provide time-series charts, hourly breakdowns, and per-page origin tracking for each form.

    hashtag
    Record Actions

    When selecting entries, it is possible to execute different actions. To select an entry, click anywhere on the entry.

    Select at least 1 record to see the available actions. By default, there are 2 possible actions:

    • Clear

    • Delete

    Umbraco Forms in the Database

    In Umbraco Forms, it is only possible to store Form data in the database.

    If you are upgrading to Umbraco 9 or later and using Forms, you should first migrate the Forms to the database using Forms 8. As of Umbraco Forms version 8.5.0 it is possible to persist all Forms data in the Umbraco database. This includes definitions for each Form and their fields, as well as workflow definitions and prevalues.

    circle-info

    Custom file system providers

    If custom file system providers are used on your project for storing Umbraco Forms dataarrow-up-right, the migration will not be able to run.

    To persist your Umbraco Forms data in the database, you will need to revert to a standard Umbraco Forms configuration. Use the default provider to store the Forms definition files in the default location.

    You need to ensure that your Forms definition files are moved from their previous location. This is a non-default file path, blob storage, or similar to the default location, App_Data/UmbracoForms, that Forms will now be using.

    Your configuration is now considered a standard configuration and you can perform the steps required for a normal migration.

    hashtag
    Enable storing Forms definitions in the database

    To persist Umbraco Forms definitions in the database, follow these steps:

    1. Upgrade to at least Umbraco Forms version 8.5.2.

    2. Open the configuration file App_Plugins\UmbracoForms\UmbracoForms.config.

    3. Locate the StoreUmbracoFormsInDb key in the <settings> section, and make sure it has the following value:

    If you are working with a Umbraco Cloud project, make sure you follow the migration steps outlined in the article.

    circle-exclamation

    Enabling the persisting of Umbraco Forms in the database is irreversible. Once you've made the change, reverting to the file approach will not be an option.

    When you save the file, the site will restart and run a migration step, migrating the files to the Umbraco database.

    hashtag
    Migrating Forms in files into a site

    You can force Forms to rerun the migration of the file-format Forms if you have a Umbraco 8 site storing Forms in the database.

    First of all, you should ensure that you have enabled the setting that persists Forms in the database, as the migration requires this (StoreUmbracoFormsInDb) key. We highly recommend testing this on a local setup before applying it to your live site.

    1. Copy over the Forms, workflows, prevaluesources, and datasource files to the site into ~\App_Data\UmbracoForms\Data.

    2. Go to the database and find the [umbracoKeyValue] table.

    3. Find the Form's row and check that the value is 1d084819-84ba-4ac7-b152-2c3d167d22bc

    The site will now try to migrate the Forms files into the database. In the umbracoTraceLog, you can follow the progress. It will throw errors if anything goes wrong. Additionally, it will log out "The Umbraco Forms DB table {TableName} already exists" for the 4 Forms tables before starting the migration.

    Property Editors

    When forms are created, editors will want to add them to pages in Umbraco. To do this they need a Document Type with a property that uses a Data Type based on a Form Picker property editor.

    Umbraco Forms provides three variations of a form picker.

    Form Pickers

    Most commonly used is Form Picker (single). This will allow the editor to select a single form for display on page.

    Rarely but feasibly, you will have a requirement to present multiple forms on a page. Should this be appropriate, you can use Form Picker (multiple).

    circle-info

    Internally this is used for presenting the list of "Allowed forms" you can select when setting up a form picker datatype.

    Finally you can provide further flexibility for the editor to select not only a form but also the theme and redirect as well. For this you will use the Form Details Picker.

    hashtag
    Configuring the Data Type

    Each property editor allows you to restrict the forms that can be chosen with the Data Type. You do this by setting either or both of the list of "Allowed folders" or "Allowed forms".

    The "Form Details Picker" also allows you to select whether a theme or redirect selection is available.

    hashtag
    Property Value Conversion

    The type of a property based on the Form Picker presented in a Razor class library is as follows:

    Option
    Description

    hashtag
    Content Delivery API Expansion

    Each reference to a form supports expansion via the Umbraco Content Delivery API, as described .

    Adding a Validation Pattern

    Customize the regular expression based validation patterns available for text fields.

    When creating a text field in Umbraco Forms, a validation pattern in the form of a regular expression can be applied. Default patterns can be removed or re-ordered, and custom ones created and added.

    hashtag
    Provided patterns

    Umbraco Forms ships with three patterns: number, email, and URL. The class names are Number, Email, and Url respectively, and all are found in the Umbraco.Forms.Core.Providers.ValidationPatterns namespace.

    hashtag
    Creating a custom validation pattern

    To create a custom format function, create a class that implements IValidationPattern. You will need to initialize five properties:

    • Alias - an alias that should be unique across the patterns and is typically camel-cased with no spaces.

    • Name - the name of the pattern that will be visible in the backoffice.

    • LabelKey - as an alternative to providing a name, a translation key can be provided. This will be used to look-up the name in the correct language for the backoffice user.

    The following example shows the implementation of a pattern for a United Kingdom postcode (credit for the to at StackOverflow).

    hashtag
    Registering the validation pattern

    As with other provider types, the validation pattern needs to be registered. There are options to add, remove, and re-order patterns.

    An example registration using the IUmbracoBuilder is shown below:

    hashtag
    Using the pattern

    With the pattern registered it will be available for selection by editors in the backoffice when they create validation for fields supporting this feature.

    Localization

    The labels, descriptions, and buttons that make up the backoffice screens for Umbraco Forms can be translated into different languages.

    When an editor chooses a language for their account, Umbraco CMS will render appropriate translations. The translations will contain a file for that language and a key for the label in question. If either of these can't be found, the label will be displayed in English (US).

    hashtag
    Language Files

    Umbraco Forms ships with translations for the following languages:

    • Czech (cs-cz.js)

    • Danish (da-dk.js)

    • Dutch (nl-nl.js)

    • French (fr-fr.js)

    • Italian (it-it.js)

    • Polish (pl-pl.js)

    • Spanish (es-es.js)

    • UK English (en-gb.js)

    • US English (en.js)

    If the language you require does not exist, it's possible to create your own by duplicating the default en.js file. You can then save it with the appropriate culture code for the language you need and replace the English text with the translated version.

    As of Forms 10, the file no longer exists on disk and is shipped as part of the Umbraco.Forms.StaticAssets NuGet package. You can open this package, either locally using , or by clicking the "Open in NuGet Package Explorer" link. You'll find the file at staticwebassets/en.js.

    Once translated, the new file should be saved somewhere in the App_Plugins folder for example App_Plugins/UmbracoFormsLocalization/. The final step is to register the localization file. This can be done by creating a umbraco-package.json like so:

    Form Information

    You can view the System information of the form in the Info tab.

    To access the Form Information:

    1. Go to the Forms section.

    2. Open a Form you wish to customize.

    3. Click Info in the top-right corner of the screen.

    hashtag
    General

    The "General" panel displays system information about the form. The date the form was created and last updated are shown. Also available are the integer and GUID identifiers that are useful when referring to the form in code.

    hashtag
    References

    Information about which pages a form is hosted on is tracked by Umbraco every time a content item is saved.

    The list of pages where the form is hosted is shown in this section.

    Block List Labels

    When working with the Block List editor, the editor experience is enhancedarrow-up-right by defining a label for the appearance of the Block.

    These labels can use Umbraco Flavored Markdown (UFM)arrow-up-right.

    An option is available to display a form's name - umbFormName.

    It should be rendered as follows, with a reference to the property alias on the block element that uses a form picker.

    {umbFormName: <property-alias>}

    If you add a reference to a property containing a form to the block's label, it will render with the form's Id.

    For example, assuming a property containing a picked form with an alias of contactForm:

    By using the markdown as follows, the form's name will be displayed instead.

    Overview Of The Field Types

    Umbraco Forms comes with a bunch of default Field Types, also known as Answer Types. You can choose from different field types when adding new fields to your Forms.

    By default, the following Field Types are available:

    • Short Answer: A textbox allows up to 250 characters.

    Setting-up Conditional Logic on Fields

    Sometimes you might have a field in your Form, that you want to show only if the user has entered a specific value in another field.

    You can achieve this setting by using conditional logic on Fields.

    hashtag
    Example

    Take a look at the following:

    Form Advanced Options

    In this article, you will find information about accessing the Forms Advanced Options and the features available to customize your Form.

    To access the Form Advanced Options:

    1. Navigate to the Forms section.

    2. Open a Form you wish to customize.

    Licensing

    Umbraco Forms is a commercial product. You can run Umbraco Forms unrestricted locally without the need for a license. Running Umbraco Forms in the public domain will require a valid license.

    Version 16 supports both the one-off purchase and (in 16.1+) subscription license.

    hashtag
    How does it work?

    Licenses are sold per domain and will also work on all subdomains. With every license, you will be able to configure two development/testing domains.

    Preparing Your Frontend

    For Umbraco Forms to work correctly, you need to include some client dependencies.

    hashtag
    Client-Side Validation

    Umbraco Forms ships with client-side form validation features provided by the .

    You can use the following Razor helper to output script tags containing the dependencies. To access this method you will need a reference to Umbraco.Forms.Web:

    Custom Markup

    This article teaches you how to customize how your Umbraco Forms are outputted.

    With Umbraco Forms, it is possible to customize the output markup of a Form, which means you have complete control over what Forms will display.

    circle-exclamation

    We recommend using to customize your Forms. This will ensure that nothing is overwritten when you upgrade Forms to a newer version.

    Prevalue Source Types Overview

    There are some default prevalue source types that can be used. In this article, we will give a quick overview of them:

    hashtag
    Get values from textfile

    Upload a textfile that contains the prevalues. Each prevalue should have its own line in the file. Once the file has been uploaded, you can find it in ~/wwwroot/App_Data/UmbracoForms/Data/PreValueTextFiles/{GUID} where the {GUID}is replaced with the pre-value ID.

    Rendering Forms

    Learn the different ways of rendering a form on your website when using Umbraco Forms.

    There are two options available for rendering a form.

    hashtag
    Rendering Using a View Component

    To display a form in your view, you can make a call to a view component. You can use a forms GUID directly or add a form dynamically by referencing a form selected via a Forms Picker.

    When selecting a theme, it can be added directly as a string or dynamically by referencing a theme picked via a Theme Picker.

    Storing Prevalue Text Files With IPreValueTextFileStorage

    Umbraco Forms contains a built-in Get value from textfile that stores the uploaded text file into the physical file system (by default in umbraco\Data\UmbracoForms\PreValueTextFiles).

    You can replace the default implementation by writing your own IPreValueTextFileStorage and registering that using e.g. builder.Services.AddUnique<IPreValueTextFileStorage, CustomPreValueTextFileStorage>() (in Program.cs or a composer).

    You can also use/inherit from PreValueTextFileSystemStorage

    Adding a Magic String Format Function

    This builds on the "" chapter

    Umbraco Forms can be used to replace placeholders within form elements with values from different sources. Sources include the HTTP request or the Umbraco page where the form is hosted.

    These values can be formatted using .

    Filter functions for common operations such as truncating a string or formatting a date or number are provided. It's also possible to create custom ones in code.

    hashtag

    Field Types

    Umbraco Forms comes with a number of Field Types to allow you to request certain data in the forms that you design & build. This documentation is to guide specific details about field types that we ship that require some detail in how they work.

    hashtag
    Date Picker

    The date picker uses a front-end library called to display a UI to pick dates from. We have added the support for the Pikaday date picker to be localized based on the page the form is rendered on. This displays the picked date in the correct locale. In JavaScript, we update a hidden field with a standard date format. This is done to send the date to the server, ensuring the record submission is stored in a standard format. This is to avoid locale mixing up dates.

    <ItemGroup>
      <PackageReference Include="Umbraco.Forms" Version="xx.x.x" />
    </ItemGroup>
    dotnet add package Umbraco.Forms
    Form information dialog
    Form general information panel
    Form relations panel
    NuGet Package Manager

    Form Picker (single)

    Single GUID representing the form's identifier.

    Form Picker (multiple)

    Collection of GUIDs representing the form identifiers.

    Form Details Picker

    Instance of the Umbraco.Forms.Core.PropertyEditors.Models.FormDetails object, which has properties for the form, theme and redirect.

    here
    Form Picker DataType Configuration
  • Pattern - the regular expression pattern.

  • ReadOnly - a flag indicating whether the pattern can be edited in the backoffice.

  • patternarrow-up-right
    Mecanikarrow-up-right
    Validation pattern
    Nuget Package Explorerarrow-up-right
    onlinearrow-up-right
    {=contactForm}
    {umbFormName: contactForm}
  • Save the file.

  • (if not you are not currently working with Forms in the database, changing the setting should be enough).
  • Change that value to {forms-init-complete}.

  • Restart the site.

  • Umbraco Forms on Cloudarrow-up-right
    circle-info

    The licenses are not bound to a specific product version. They will work for all versions of the related product, but version 17+ will only be available through a subscription-based license (see announcementarrow-up-right).

    Let's say that you have a license configured for your domain, mysite.com, and you've configured two development domains, devdomain.com and devdomain2.com.

    The license will cover the following domains:

    • localhost

    • *.mysite.com

    • www.mysite.com

    • mysite.com.local

    • devdomain.com

    • www.devdomain.com

    • devdomain2.com

    • www.devdomain2.com

    circle-info

    You can have only 1 license per Umbraco installation.

    hashtag
    What does a license cover?

    There are a few differences as to what the licenses cover:

    • A single license covers the installation of Umbraco Forms in 1 production domain, as well as in 2 development domains.

    • The production domain includes all subdomains (e.g. *.mysite.com), as well as the .local extension (e.g. mysite.com.local).

    • The development domains work with or without the www subdomain.

    • The license allows for an unlimited number of forms.

    • The license also includes localhost as a valid domain.

    circle-info

    If you have multiple domains pointing at the same installation, you have the option to purchase and add additional domains to your license.

    Each additional domain includes 1 live domain and 2 development/testing domains.

    This is an add-on domain for existing licenses. Refunds will not be given for this product.

    hashtag
    Configuring your license

    You can look at the pricing, features, and purchase the license on the Umbraco Formsarrow-up-right page.

    hashtag
    Add additional domains

    If you require to add additional domains to the license, reach out the sales teamarrow-up-right with your request and they will manage this process.

    hashtag
    Installing subscription license product key

    Once you've purchased your subscription license with the correct domains, you are ready to configure the license key on your Umbraco installation.

    The license key should be added to your configuration using product ID: Umbraco.Forms.

    For detailed instructions on how to install and configure your license, including version-specific examples and additional configuration options, see the Configure Licensesarrow-up-right article.

    hashtag
    Federal Information Processing Standards (FIPS) Compliant Environments

    The algorithm used to decrypt Forms licenses is not supported on locked down FIPS compliant environments, such as those used in the defense industry.

    If you are in this situation and unable to resolve it via configuration of the environment, reach out to Umbraco Support.

    We have the possibility of generating and providing Forms licenses using alternate algorithms.

    Apply the following configuration with the appropriate algorithm - DES (the default), TripleDES, or AES:

    Url is a parameter passed into the method. It’s defined as a property on the base view model for an Umbraco template, so it will be automatically available in your Razor views.

    Alternatively, you can add the dependencies to the body tag:

    All dependencies originate from your Umbraco Forms installation, which means that no external references are needed.

    If you want to modify the rendering of the scripts, you can provide a object parameter named htmlAttributes. The contents of the object will be written out as HTML attributes on the script tags.

    You can use this to apply async or defer attributes. For example:

    If using async, please make sure to disable the Forms client-side validation framework check. This is necessary as it's not possible to guarantee that the asynchronous script will load in time to be recognized by the check. This can then cause a false positive warning.

    hashtag
    Validation Using jQuery

    It is possible to use jQuery as your validation framework instead of the ASP.NET Client Validation library.

    To use jQuery validation, add the following client dependencies:

    • jQuery (JavaScript library)

    • jQuery validate (jQuery plugin that provides client-side Form validation)

    • jQuery validate unobtrusive (Add-on to jQuery Validation that provides unobtrusive validation via data-* attributes)

    Be sure to remove any calls to @Html.RenderUmbracoFormDependencies(Url).

    The easiest way to add the dependencies is to fetch them from a CDNarrow-up-right. There are various CDN services you can use:

    • For example: Microsoft CDNarrow-up-right.

    • Other CDN services you might want to look at are https://www.jsdelivr.com/ and https://cdnjs.com/about, which may offer better performance and more reliable service.

    To add the three client dependencies, see the examples below:

    Example within head tags.

    Example within body tags.

    When adding the script to the bottom of the page, you will also need to render the scripts. For more information, see Rendering Forms Scripts article.

    ASP.NET Client Validation libraryarrow-up-right
    to change the underlying
    IFileSystem
    that's used to store the prevalue text files.

    hashtag
    Move files to Media file system

    You can use the following composer to move the prevalue text files into the media file system. If the media file system is using Azure Blob Storage, this will remove the files from the local physical file system.

    You need to manually move the existing files from umbraco\Data\UmbracoForms\PreValueTextFiles to your media storage. The final file path/URL will look like ~/media/PreValueTextFiles/{GUID}/{filename.txt} and be accessible from the browser.

    hashtag
    Move files to Azure Blob Storage

    First, install Umbraco.StorageProviders.AzureBlobarrow-up-right and configure the Forms storage container, for example by adding the following to your appsettings.json:

    Next, add the following composer that adds the Forms storage container and stores the prevalue text files into Azure Blob Storage (in forms/PreValueTextFiles/{GUID}/{filename.txt}):

    You need to manually move the existing files from umbraco\Data\UmbracoForms\PreValueTextFiles to your storage container. If you've disabled public access, the stored files are not accessible from the browser.

    Prevalue Source Type
    Creating a custom format function

    To create a custom format function, create a class that implements IParsedPlaceholderFormatter.

    The FunctionName property provides the name of the function that will be used within the form's magic string.

    The FormatValue property parses the provided value and arguments and returns the formatted value as a string.

    The following example shows the implementation of a function that bounds an integer value. It takes two arguments, a minimum and maximum value. If the value read from the magic string source is numeric, and fits within the two bounds, it is returned. Otherwise, either the minimum or maximum value is returned depending on whether the value is lower or higher than the bounds respectively.

    hashtag
    Registering the custom format function

    As with other provider types, the custom function needs to be registered. An example registration using the IUmbracoBuilder is shown below:

    hashtag
    Using the custom format function

    The format function can be used within a form's magic string in the same way as the ones provided with Umbraco Forms.

    For the example provided, it would be used like this:

    adding a type to the provider model
    Magic Strings
    filter functions
    To achieve this a new Razor partial view is included
    /Views/Partials/Forms/DatePicker.cshtml
    . Once on a page with a form that includes a Date Picker, it also includes the MomentJS library to assist with date locale formatting. Additionally, there are appropriate changes to Pikaday.js to support the locales. If you wish to use a different DatePicker component this is the file that you would customize to your needs.

    hashtag
    Date Picker configuration of the year range

    The DatePicker has one configuration setting to control the number of year shown. The default is 10 years which makes the picker unusable for picking birth dates.

    Go to your appsettings.json and add:

    You can then change the DatePickerYearRange to a higher number (for example 100).

    PikaDay.jsarrow-up-right
      "Umbraco": {
        "CMS": {
            ...
        },
        "Forms": {
          "FieldTypes": {
            "DatePicker": {
              "DatePickerYearRange": 12
            }
          }
         }
        }
    using Umbraco.Forms.Core.Interfaces;
    namespace Umbraco.Forms.TestSite.Business.ValidationPatterns
    {
        public class UkPostCode : IValidationPattern
        {
            public string Alias => "ukPostCode";
            public string Name => "UK Post Code";
            public string LabelKey => string.Empty;
            public string Pattern => @"^([a-zA-Z]{1,2}[a-zA-Z\d]{1,2})\s(\d[a-zA-Z]{2})$";
            public bool ReadOnly => true;
        }
    }
    public static IUmbracoBuilder AddCustomProviders(this IUmbracoBuilder builder)
    {
        builder.FormsValidationPatterns()
            .Append<UkPostCode>();
        return builder;
    }
    {
      "$schema": "../../umbraco-package-schema.json",
      "name": "Umbraco.Forms.Extensions",
      "extensions": [
        {
          "type": "localization",
          "alias": "UmbracoForms.Localize.DeDE",
          "name": "	German (Germany)",
          "meta": {
            "culture": "de-de"
          },
          "js": "/App_Plugins/UmbracoFormsLocalization/de-de.js"
        }
      ]
    }
    <setting key="StoreUmbracoFormsInDb" value="True" />
      "Umbraco": {
        "Licensing": {
          "LicenseEncodeAndDecodeAlgorithm": "DES|TripleDES|AES"
        },
    @using Umbraco.Forms.Web
    <head>
        @Html.RenderUmbracoFormDependencies(Url)
    </head>
    @using Umbraco.Forms.Web
    ...
    
    <body>
        @Html.RenderUmbracoFormDependencies(Url)
    </body>
    @Html.RenderUmbracoFormDependencies(Url, new { @async = "async" })
    <head>
        <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.0.0.min.js"></script>
        <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.19.2/jquery.validate.min.js"></script>
        <script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"></script>
    </head>
    <body>
        <!-- Page content here -->
    
        <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.0.0.min.js"></script>
        <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.19.2/jquery.validate.min.js"></script>
        <script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"></script>
    </body>
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Cms.Core.IO;
    using Umbraco.Cms.Core.Scoping;
    using Umbraco.Forms.Core.Data;
    
    public class PreValueTextFileSystemStorageComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
            => builder.Services.AddUnique<IPreValueTextFileStorage>(factory => new PreValueTextFileSystemStorage(
                factory.GetRequiredService<MediaFileManager>().FileSystem,
                factory.GetRequiredService<IScopeProvider>(),
                "PreValueTextFiles"));
    }
    {
      "Umbraco": {
        "Storage": {
          "AzureBlob": {
            "Forms": {
              "ConnectionString": "UseDevelopmentStorage=true",
              "ContainerName": "sample-container"
            }
          }
        }
      }
    }
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Cms.Infrastructure.Scoping;
    using Umbraco.Forms.Core.Data;
    using Umbraco.StorageProviders.AzureBlob.IO;
    
    public class PreValueTextFileSystemStorageComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
            => builder.AddAzureBlobFileSystem("Forms", options => options.VirtualPath = "~/forms")
                .Services.AddUnique<IPreValueTextFileStorage>(factory => new PreValueTextFileSystemStorage(
                    factory.GetRequiredService<IAzureBlobFileSystemProvider>().GetFileSystem("Forms"),
                    factory.GetRequiredService<IScopeProvider>(),
                    "PreValueTextFiles"));
    }
    using System.Globalization;
    using Umbraco.Forms.Core.Interfaces;
    
    namespace Umbraco.Forms.Core.Providers.ParsedPlacholderFormatters
    {
        public class BoundNumber : IParsedPlaceholderFormatter
        {
            public string FunctionName => "bound";
    
            public string FormatValue(string value, string[] args)
            {
                if (args.Length != 2)
                {
                    return value;
                }
    
                if (!int.TryParse(args[0], out var min) || !int.TryParse(args[1], out var max))
                {
                    return value;
                }
    
                if (int.TryParse(value, out int valueAsInteger) ||
                    int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out valueAsInteger))
                {
                    if (valueAsInteger < min)
                    {
                        return min.ToString();
                    }
    
                    if (valueAsInteger > max)
                    {
                        return max.ToString();
                    }
    
                    return valueAsInteger.ToString();
                }
    
                return value;
            }
        }
    }
    public static IUmbracoBuilder AddCustomProviders(this IUmbracoBuilder builder)
    {
        builder.FormsParsedPlaceholderFormatters()
            .Add<BoundNumber>();
        return builder;
    }
    [#field | bound: 1: 10]

    The Export dialog opens. Choose a format such as Excel File to export the Form records to.

    Export dialog
  • Click Export.

  • Click Save.

  • Analytics
    Entries viewer
    Filter
    Export Entries
    Record bulk actions
    Long Answer: A bigger text field that allows multiline text and more than 250 characters.
    Textarea
  • Date: Displays a picker that allows the user to select a date.

    Datepicker
  • Checkbox: Displays a single checkbox that can be checked or not.

    Checkbox
  • File Upload: Allows the user to select and upload a local file.

    File upload
  • Password: Allows typing a password. The input is not visible when typing.

    Password field
  • Multiple Choice: Displays a list of items with a checkbox for each item where the user can select multiple options.

    Checkboxlist
  • Data Consent: A field for the purpose of asking for data consent. By default, this field is added to all new Forms.

    Data Consent
  • Dropdown: Displays a list of items in a drop-down box where the user can select a single option.

    Dropdownlist
  • Single Choice: Displays a list of items with a radio button for each item where the user can select a single option.

    Single choice
  • Title and Description: Displays a read-only title and description for a set of form fields.

    Title and description
  • Rich Text: Displays read-only formatted text that can be used to provide additional information and links within a form.

    Rich text
  • Hidden: A hidden field allows developers to include data that cannot be seen or modified by users when a Form is submitted.

    Hidden
  • Recaptcha V2: The field displays a single checkbox for the user to select to validate the Form.

    reCAPTCHA v2
  • Recaptcha V3 with Score: This field returns a score for each request without user interaction. The score is based on user interactions with the site and enables you to take an appropriate action for your site based on the score.

    reCAPTCHA v3
  • Recaptcha Enterprise with Score: This field returns a score for each request without user interaction. The score is based on user interactions with the site and enables you to take an appropriate action for your site based on the score.

  • Textfield
    In this case, it makes sense to only show the email or phone field when the corresponding option is selected in the How should we contact you? field.

    To enable conditions for the Email and Phone fields, do the following:

    1. Click the cog wheel next to the Email and Phone field. The Edit question dialog opens.

    2. Enable Conditions. The condition field displays more options:

      Enable Conditions
    3. Set the appropriate conditions and click Submit.

    hashtag
    Action and Logic Types

    There are two Action Types:

    • Show: the field will be displayed if the rules match

    • Hide: the field will be hidden if the rules match

    Next up, you'll need to specify the Logic Type. This setting is only important if you have multiple rules.

    • All: All of the rules must match

    • Any: Any of the rules may match

    hashtag
    Adding a new condition

    When adding a new condition, you'll need to select the field where you want to evaluate the value and can select an operator.

    In this example, we only want to show the Phone field if the value of the How should we contact you field is Phone.

    Setup rule

    Similarly, you can display the Email field, if the value of the How should we contact you field is Email. You can see the conditions added to each field in the Forms designer:

    See conditions in the Forms designer

    hashtag
    Result

    When both the conditions have been set as shown above, this is how it will look on the frontend:

    Frontend Example

    In this example, we have only selected Phone but it is possible to choose both Phone* and Email and display both the fields.

    hashtag
    Conditions for Pages and Fieldsets

    As well as showing or hiding a field based on conditions, you can also apply conditions to groups of fields (known as fieldsets) or to pages. The process is the same as described above.

    When applying a condition to a page, effectively you are controlling the display of the submit button (for a single-page form) or the next/previous buttons (available on multi-page forms). In this way you can ensure that the entry so far is complete before accepting it or allowing the user to move onto the next page.

    hashtag
    Conditions for Dates

    To use less-than or greater-than conditions on dates, you must change the date format.

    By default, date is shown like September 5, 2025. This needs to change to a format only containing numbers: 09/05/2025.

    To change the default format setting for dates, follow the steps below:

    1. Open the appSettings.json file.

    2. Change the DatePickerFormat value to L (the default is LL):

    Example Form

    Click Advanced in the top-right corner of the screen.

    circle-info

    The advanced options for forms are only available when configured to display.

    hashtag
    Validation Rules

    When creating forms you can add validation to individual fields, making them mandatory or applying a regular expression pattern. You can provide validation rules for the entire form via the advanced options. This allows you to validate expressions based on multiple fields. For example, "these two email fields should be the same", or "this date should be after this other one".

    Validation rules

    To add new rules, you need to provide the rule definition, an error message and select a field to which the message will be associated. Once created you can click to edit or delete them from the list.

    Crafting the rule definition itself requires use of JSON logicarrow-up-right along with placeholders for the field or fields that are being validated.

    hashtag
    Examples

    One example use case would be ensuring that two fields match each other, perhaps when asking for a user's email address. Given two fields on the form, one with the alias of email and the other compareEmail, the rule would be:

    A slightly more complex example could be with two dates, where, if provided, you want to ensure the second date is later than the first. So given fields with aliases of startDate and endDate a rule would look like this:

    Rules can be nested too. In this final illustrative example, we have two fields. One with the alias choose is a drop-down list with two values: A and B. The second field with alias test we want to be completed only if the user selects B. So we create a rule that is valid only if A is selected OR B is selected AND test is completed.

    Overall, you can create rules of varying complexity, using comparisons between fields and static values.

    When the form is rendered, these validation rules will be applied on both the client and server-side. In this way, you can ensure the submission is only accepted if it meets the requirements.

    hashtag
    Customizing the Default Views

    The razor macro uses some razor views to output the Form:

    • 1 view for each fieldtype

    • 1 view for the scripts

    • 1 view for the rest of the Form

    You can find the default views in the ~\Views\Partials\Forms\Themes\default folder.

    To avoid your custom changes to the default views from being overwritten, you need to copy the view you want to customize into your theme folder, e.g. ~\Views\Partials\Forms\Themes\YourTheme, and edit the file in YourTheme folder.

    hashtag
    Form.cshtml

    This is the main view responsible for rendering the Form markup.

    The view is separated into two parts, one is the actual Form and the other will be shown if the Form is submitted.

    This view can be customized, if you do so it will be customized for all your Forms.

    hashtag
    Script.cshtml

    This view renders the JavaScript that will take care of the conditional logic, customization won't be needed here.

    hashtag
    FieldType.*.cshtml

    The rest of the views start with FieldType, like FieldType.Textfield.cshtml and those will output the fields. There is a view for each default fieldtype like textfield, textarea, checkbox, etc)

    Contents of the FieldType.Textfield.cshtml view (from the default theme):

    Umbraco Forms uses ASP.NET Unobtrusive Validation which is why you see attributes like data-val and data-val-required.

    This can be customized but it's important to keep the ID of the control to @Model.Id since that is used to match the value to the Form field. For fields that are conditionally hidden, without an ID of @Model.Id the control won't be shown when the conditions to show the field are met. An ID needs to be added to text controls such as headings and paragraphs.

    The view model for the partial view for a field is FieldViewModel. This defines properties that may be useful when creating new themes or custom fields, some of which are shown in the code samples above. Others include:

    • AdditionalSettings - a dictionary of the settings keys and values populated for the form field. These can be retrieved in typed form by key using e.g. Model.GetSettingValue<int>("MaximumLength", 255);.

    The following are available on the model but only populated for fields that support file upload:

    • AllowAllUploadExtensions- a boolean indicating whether all file extensions are permitted for upload.

    • AllowedUploadExtensions- a collection of strings indicating the file extensions that are permitted for upload.

    • AllowMultipleFileUploads- a boolean indicating whether selecting multiple files for upload is allowed.

    hashtag
    Customizing for a Specific Form

    It is also possible to customize the markup for a specific Form.

    You will need to create folder using the ID of the Form: ~\Views\Partials\Forms\{FormId} (find the id of the Form in the URL when you are viewing the Form in the backoffice.)

    Form GUID

    As an example if your Form ID is 0d3e6b2d-db8a-43e5-8f28-36241d712487 then you can overwrite the Form view by adding the Form.cshtml file to the directory. Start by copying the default one and then making your custom changes: ~\Views\Partials\Forms\0d3e6b2d-db8a-43e5-8f28-36241d712487\Form.cshtml.

    You can also overwrite views for one or more fieldtypes by adding the views to the Fieldtypes folder: ~\Views\Partials\Forms\0d3e6b2d-db8a-43e5-8f28-36241d712487\Fieldtypes\Fieldtype.Textfield.cshtml.

    Themes
    Get values from textfile

    hashtag
    Umbraco Documents

    Allows you to use content nodes from a specific source as prevalues. Configure the following options in the Details section:

    • Define the root node by either:

      • Selecting the type of item the picker should target such as Content, Media, or Members, or

      • Specifying a dynamic root.

    • Enable Use current page as root instead of choosing a specific root node. Preview is not available when this setting is enabled.

    • Choose a specific Document type, if the selected root node contains a different Document Type.

    • Select the Value field to define the value stored for the prevalue.

    • Select the Caption field to define the label shown in the list.

    • Enable List all Descendants of the selected root node to include all child nodes beneath the root.

    • Select Order by from the drop-down list to display how the prevalue list is sorted.

    • Select your preferred Cache option for caching the list of prevalues when rendering in a form.

    Umbraco Documents

    hashtag
    SQL Database

    Connect to a OleDB compatible database table and construct a prevalue source from it. Once selected, it will be editable from the Forms interface.

    Configure the following options in the Details section:

    • Connection string (either choose one from your web.config or add another from a textfield).

    • Connection String from configuration

    • Table Name

    • Key Column

    • Value Column

    • Caption Column

    • Select your preferred Cache option for caching the list of prevalues when rendering in a form.

    SQL Database

    hashtag
    Umbraco Data Type Prevalues

    Choose an Umbraco Data Type to use its configured prevalue collection.

    In the example below, the prevalue collection from a Data Type called Home - Font - Radio button is used:

    Data Type prevalues
    This example uses a Forms Picker with form as alias, and a Theme Picker with theme as alias.
    @await Component.InvokeAsync("RenderForm", new
    

    This example hard-codes the GUID of a form and the name of the theme.

    Six parameters can be provided:

    • formId is the GUID of a form.

    • theme is the name of a theme. If not provided, the default theme is used (see Themes).

    • includeScripts indicates whether scripts should be rendered with the form (see .

    • recordId is an optional existing record GUID, used if editing records via the website is

    • redirectToPageId is an optional GUID for a content page that, if provided, is redirected to once the form has been submitted. It will be used in preference to post-submission behavior defined on the form itself.

    • additionalData is an optional dictionary of string values. When provided it will be used as a source for . The data will be associated with the created record and made available for custom logic or update within workflows.

    The following example shows how the additionalData parameter is used:

    hashtag
    Rendering Using a Tag Helper

    If you prefer a tag helper syntax, you can use one that ships with Umbraco Forms.

    Firstly, in your _ViewImports.cshtml file, add a reference to the Umbraco Forms tag helpers with:

    Then in your view you can use:

    @await Component.InvokeAsync("RenderForm", new { formId = @Model.Form, 
                                                     theme = @Model.Theme,
                                                     includeScripts = false })

    Analytics

    View analytics for your Umbraco Forms to understand submission trends, workflow performance, and where your forms are being used.

    circle-info

    This feature is available from Umbraco Forms 17.3.

    Umbraco Forms includes built-in analytics that provide insight into how your forms are performing. You can view submission trends over time, monitor workflow success rates, and identify which pages are driving submissions.

    hashtag
    Permissions

    To view form analytics, you need:

    • Access to the Forms section in the backoffice.

    • The View Entries permission for the form.

    Users will only see analytics for forms they have permission to manage.

    hashtag
    Accessing Analytics

    There are two ways to access form analytics in the backoffice.

    hashtag
    From the Forms Section Menu

    In the Forms section, click the Analytics menu item in the sidebar. This opens the analytics overview where you can browse data across all your forms.

    hashtag
    From a Specific Form

    You can navigate directly to analytics for a specific form:

    1. Go to the Forms section.

    2. Right-click on a form or open its action menu.

    3. Select View Analytics.

    This opens the Analytics tab on the form workspace, showing analytics data for that form only.

    circle-info

    The Analytics tab appears as a workspace view on existing forms. It is not available when creating a new form that has not yet been saved.

    hashtag
    Controls

    The analytics view provides controls to adjust the data displayed.

    hashtag
    Date Range

    Use the date range picker to select the time period for the analytics data. You can choose from preset ranges such as Last 7 days or Last 30 days, or set a custom date range. You can also enable a comparison date range to compare the current period against a previous one.

    hashtag
    Group By

    Use the Group by control to change how the data is aggregated in the time-series charts:

    • Day — shows data points for each day in the selected range.

    • Week — groups data by week.

    • Month — groups data by month.

    hashtag
    Overview Table

    The overview table lists all forms you have access to with summary statistics for the selected date range. This includes entry counts, workflow counts, workflow errors, and source pages.

    Click a form name to navigate to its detailed analytics view.

    hashtag
    Analytics Widgets

    When viewing analytics for a specific form, the view displays four widgets providing different perspectives on form performance.

    hashtag
    Submissions

    A time-series chart showing the number of form submissions and failed workflows over the selected date range.

    hashtag
    Submissions by Hour

    A chart showing the distribution of form submissions across the 24 hours of the day. This helps identify peak submission times and can inform decisions about when to schedule maintenance or review submissions.

    hashtag
    Workflow Statistics

    A table listing each workflow attached to the form. It shows the number of times each workflow was triggered, succeeded, and failed. Use this to monitor the health of your form processing workflows.

    hashtag
    Origins

    A time-series chart showing form submissions broken down by the page where the form was submitted. This is useful when the same form is placed on multiple pages across your site, as it shows which pages are driving the most submissions.

    Below the chart, a table lists each source page with its name, URL, and entry count.

    hashtag
    Data Processing

    Analytics data is pre-aggregated by a background process that runs daily. This means the analytics views load quickly even for forms with a large number of submissions.

    When forms are first installed or upgraded to a version that includes analytics, the background process will aggregate historical submission data. This may take some time depending on the volume of existing records.

    circle-info

    The analytics data processing is enabled by default. It can be configured or disabled via the AnalyticsProcessing settings in appsettings.json. For more details, see the article.

    Adding A Workflow Type To Umbraco Forms

    This builds on the "adding a type to the provider model" chapter

    Add a new class to your project and have it inherit from Umbraco.Forms.Core.WorkflowType, and implement the class. For this sample, we will focus on the execute method. This method processes the current record (the data submitted by the form) and have the ability to change data and state.

    hashtag
    Information available to the workflow

    hashtag
    Record information

    The ExecuteAsync() method gets a WorkflowExecutionContext which has properties for the related Form, Record, and FormState. This parameter contains all information related to the workflow.

    The Record contains all data and metadata submitted by the form. As shown in the example above, you can iterate over all RecordField values in the form. You can also retrieve a specific record field by alias using the following method:

    Having obtained a reference to a record field, the submitted value can be retrieved via:

    The ValuesAsString will JSON escape the result by default. If you do not want this escaping to occur, pass false as the parameter.

    If the field stores multiple values, they are delimited with a comma. In many cases, you can safely split on that delimiter to obtain the individual values. However, this can lead to issues if the prevalues being selected also contain commas. If that's a concern, the following extension method is available in Umbraco.Forms.Core.Extensions to correctly parse the selected prevalues:

    hashtag
    Form and state information

    The Form references the form the record is from and FormState provides its state (submitted or approved).

    Other context, such as the current HttpContext, if needed can be passed as constructor parameters (for example: the HttpContext can be accessed by injecting IHttpContextAccessor).

    hashtag
    Registering the workflow type

    To use the new workflow type, you will need to register it as part of application startup.

    Defining And Attaching Prevalue Sources

    Prevalue sources are a way to pre-define and/or retrieve a list of items from a certain source. They can be added in any field types that include some kind of list like Dropdown and Multiple/Single Choice lists.

    hashtag
    Setting up a Prevalue Source

    Prevalue sources can be managed in the Prevalue sources folder available in the Forms section.

    Prevalue source tree

    To set a prevalue source:

    1. Go to the Forms section.

    2. Click ... next to the Prevalue Sources folder.

    3. Click Create.

    hashtag
    Configuring the Prevalue Source

    Depending on the prevalue source type you choose, you'll need to provide some additional settings. For this article, we will select Get values from textfile.

    1. Select Get values from textfile from the Choose prevalue source type pane.

    2. Enter a Name for the prevalue source type. Let's call it My Prevalue Source.

    3. Now, create a file containing the list to use as prevalues. For example: a .txt file containing the following values:

    If you would like to have different values presented to your users from the value stored, you can provide two values per line, separated with a vertical bar (|), e.g.:

    In this case the user would pick from a list showing the captions, but the single integer values would be stored with the record.

    This can be useful if the recorded entries are used in any subsequent workflows or business processes, where particular values, that aren't appropriate for the user to select from, are required.

    hashtag
    Defining Cache Options for the Prevalue Source

    Sometimes retrieving the list of options for a prevalue source can be an expensive operation. If the source depends on data from external systems, it could be that the list changes regularly or rarely.

    Given the variation here, we allow you to select an appropriate level of caching for the list of options.

    You can choose between:

    • No Caching - no caching will be applied and the list of options will be retrieved from source on every request. You will likely only want to choose this option if the information changes frequently and it's important that the latest is presented to website visitors.

    • Cache For Specified Time - the list will be cached for the period of time provided.

    • Cache With No Expiry

    hashtag
    Attaching a Prevalue Source to a Field

    Once a prevalue source has been created, it can be used while building Forms in the Forms designer.

    Example: Let's add a Multiple Choice field type in our Form.

    If there is at least one prevalue source defined in the project, the Prevalues source will contain a dropdown from where you can choose the predefined value.

    Once you have selected the prevalue source, the values are rendered in the Forms designer from the attached source.

    Rendering Forms Scripts

    Forms output some JavaScript which is by default rendered right below the markup.

    In many cases, you might prefer rendering your scripts at the bottom of the page. For example, before the closing </body> tag. This generally improves site performance.

    In order to render your scripts where you want, you need to add a snippet to your template. Make sure you add it below your scripts, right before the closing </body> tag.

    By default, Forms uses HttpContext.Items for tracking the forms rendered on a page. The stored values are used when rendering the form scripts and associated data.

    The following snippet should be used.

    If you have changed the configuration value of TrackRenderedFormsStorageMethod to use the legacy behavior TempData, the snippet is:

    Read more about the TrackRenderedFormsStorageMethod configuration option in the article.

    If you prefer to use a tag helper, that's an option too.

    Firstly, in your _ViewImports.cshtml file, ensure you have a reference to the Umbraco Forms tag helpers with:

    Then instead of reading from TempData and invoking the view component directly, you can use:

    This will use the appropriate storage method that you have configured.

    hashtag
    Using Form Scripts Alongside Validation Dependencies

    When setting up templates for Umbraco Forms, two separate script-rendering methods are involved, and both are required for forms to work correctly.

    @Html.RenderUmbracoFormDependencies(Url), covered in the article, renders client-side validation scripts such as jQuery Validate and unobtrusive validation. This goes in the <head> of your template.

    The <umb-forms-render-scripts /> tag helper (or the equivalent view component calls shown above) renders form-specific scripts covering conditional field logic, field behaviors, and any theme JavaScript. This goes before the closing </body> tag.

    Because both methods output <script> tags, they can appear to duplicate each other. The script contents are entirely different, however, and serve distinct purposes. A complete template uses both:

    Omitting RenderUmbracoFormDependencies will break client-side validation. Omitting <umb-forms-render-scripts /> will break conditional fields and theme behaviour.

    hashtag
    Enabling ExcludeScripts

    If you do not want to render the associated scripts with a Form, you need to explicitly say so. You need to make sure ExcludeScripts is checked/enabled, whether you are inserting your Form using a macro or adding it directly in your template.

    To enable ExcludeScripts:

    • Using the Insert Form with Theme macro:

    • While inserting Forms directly in your template:

    circle-info

    ExcludeScripts = "1" prevents the associated scripts from being rendered. Any other value, an empty value, or if the parameter is excluded, will render the scripts on the Form.

    Working With Record Data

    Developer documentation on working with Forms record data.

    Umbraco Forms includes some helper methods that return records of a given Form, which can be used to output records in your templates using razor.

    hashtag
    Available Methods

    The methods can be found by injecting the Umbraco.Forms.Core.Services.IRecordReaderService interface. For performance reasons, all these methods are paged.

    Attaching Workflows

    In this article, you can learn how to add extra functionality to your Form by attaching workflows.

    Workflows are a way of defining actions after your Form is submitted like sending an email or creating a content node.

    hashtag
    Default Workflow

    By default, when a Form is submitted the record data is stored in the database. This can be configured in the of the Forms settings.

    Apply keys and indexes for forms in the database

    hashtag
    Reverting the application of keys and indexes

    "Forms": {
      "FieldTypes": {
        "DatePicker": {
          "DatePickerFormat": "L"
        }
      }
    }
    {
      "==": [
        "{email}",
        "{compareEmail}"
      ]
    }
    {
      "or": [
        {
          "==": [
            "{startDate}",
            ""
          ]
        },
        {
          "==": [
            "{endDate}",
            ""
          ]
        },
        {
          ">": [
            "{endDate}",
            "{startDate}"
          ]
        }
      ]
    }
    {
      "or": [
        {
          "==": [
            "{choose}",
            "A"
          ]
        },
        {
          "and": [
            {
              "==": [
                "{choose}",
                "B"
              ]
            },
            {
              "!=": [
                "{test}",
                ""
              ]
            }
          ]
        }
      ]
    }
    @model Umbraco.Forms.Mvc.Models.FieldViewModel
    @using Umbraco.Forms.Mvc
    
    <input type="text"
        name="@Model.Name"
        id="@Model.Id"
        class="@Html.GetFormFieldClass(Model.FieldTypeName) text"
        value="@Model.ValueAsHtmlString"
        maxlength="500"
        @{if(string.IsNullOrEmpty(Model.PlaceholderText) == false){<text>placeholder="@Model.PlaceholderText"</text>}}
        @{if(Model.Mandatory || Model.Validate){<text>data-val="true"</text>}}
        @{if (Model.Mandatory) {<text> data-val-required="@Model.RequiredErrorMessage"</text>}}
        @{if (Model.Validate) {<text> data-val-regex="@Model.InvalidErrorMessage" data-val-regex-pattern="@Html.Raw(Model.Regex)"</text>}}
    />
    var additionalData = new Dictionary<string, string> { { "foo", "bar" }, { "buzz", "baz" } };
    @await Component.InvokeAsync("RenderForm", new { formId = @Model.Form, theme = @Model.Theme, includeScripts = false, additionalData })
    @addTagHelper *, Umbraco.Forms.Web
    @if (Model.Form.HasValue)
    {
        var additionalData = new Dictionary<string, string> { { "foo", "bar" }, { "buzz", "baz" } };
        <umb-forms-render form-id="@Model.FormId.Value" 
                          theme="@Model.FormTheme" 
                          exclude-scripts="true" 
                          additional-data="@additionalData" />
    }
    using Serilog;
    using System;
    using System.Collections.Generic;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Data.Storage;
    using Umbraco.Forms.Core.Enums;
    using Umbraco.Forms.Core.Persistence.Dtos;
    using Microsoft.Extensions.Logging;
    using Umbraco.Core.Composing;
    
    namespace MyFormsExtensions
    {
        public class TestWorkflow : WorkflowType
        {
            private readonly ILogger<TestWorkflow> _logger;
    
            public TestWorkflow(ILogger<TestWorkflow> logger)
            {
                _logger = logger;
    
                this.Id = new Guid("ccbeb0d5-adaa-4729-8b4c-4bb439dc0202");
                this.Name = "TestWorkflow";
                this.Description = "This workflow is just for testing";
                this.Icon = "icon-chat-active";
                this.Group = "Services";
            }
    
            public override Task<WorkflowExecutionStatus> ExecuteAsync(WorkflowExecutionContext context)
            {
                // first we log it
                _logger.LogDebug("the IP " + context.Record.IP + " has submitted a record");
    
                // we can then iterate through the fields
                foreach (RecordField rf in context.Record.RecordFields.Values)
                {
                    // and we can then do something with the collection of values on each field
                    List<object> vals = rf.Values;
    
                    // or get it as a string
                    rf.ValuesAsString(false);
                }
    
                //Change the state
                context.Record.State = FormState.Approved;
    
                _logger.LogDebug("The record with unique id {RecordId} that was submitted via the Form {FormName} with id {FormId} has been changed to {RecordState} state",
                   context.Record.UniqueId, context.Form.Name, context.Form.Id, "approved");
    
                return Task.FromResult(WorkflowExecutionStatus.Completed);
            }
    
            public override List<Exception> ValidateSettings()
            {
                return new List<Exception>();
            }
        }
    }
    {
    formId
    =
    Guid
    .
    Parse
    (
    "
    <form guid>
    "
    ),
    theme = "default",
    includeScripts = false })
    Rendering Scripts
    enabled in configuration
    "magic string" replacements
    Configuration
    Analytics overview showing a submissions chart and forms table
    Date range picker with preset options and comparison toggle
    Analytics overview table showing all forms with entry counts and statistics
    Form analytics showing the submissions chart with entries and workflow errors
    Hourly distribution chart and workflow statistics table
    Origins chart and table showing submissions by source page
    Configuration
    Preparing Your Frontendarrow-up-right
    Exclude scripts
    /*
     Applies recommended primary keys, foreign keys and indexes to Umbraco Forms tables relating to "forms in the database" (i.e.
     when configuration key StoreUmbracoFormsInDb = true).
     This replicates for SQL Server the migration AddFormKeysAndIndexes.
     */
    
    -- Adds unique constraint to UFForms.
    ALTER TABLE dbo.UFForms
    ADD CONSTRAINT UK_UFForms_Key UNIQUE NONCLUSTERED 
    (
    	[Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    -- Adds unique constraint to UFDataSource.
    ALTER TABLE dbo.UFDataSource
    ADD CONSTRAINT UK_UFDataSource_Key UNIQUE NONCLUSTERED 
    (
    	[Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    -- Adds unique constraint to UFPrevalueSource.
    ALTER TABLE dbo.UFPrevalueSource
    ADD CONSTRAINT UK_UFPrevalueSource_Key UNIQUE NONCLUSTERED 
    (
    	[Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    -- Adds unique constraint to UFWorkflows.
    ALTER TABLE dbo.UFWorkflows
    ADD CONSTRAINT UK_UFWorkflows_Key UNIQUE NONCLUSTERED 
    (
    	[Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    -- Adds index on join field in UFWorkflows.
    CREATE NONCLUSTERED INDEX IX_UFWorkflows_FormId ON dbo.UFWorkflows
    (
    	FormId ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    hashtag
    GetApprovedRecordsFromPage

    Returns all records with the state set to approved from all Forms on the Umbraco page with the id = pageId .

    hashtag
    GetApprovedRecordsFromFormOnPage

    Returns all records with the state set to approved from the Form with the id = formId on the Umbraco page with the id = pageId as a PagedResult<Record>.

    hashtag
    GetApprovedRecordsFromForm

    Returns all records with the state set to approved from the Form with the ID = formId as a PagedResult<Record>.

    hashtag
    GetRecordsFromPage

    Returns all records from all Forms on the Umbraco page with the id = pageId as a PagedResult<Record>.

    hashtag
    GetRecordsFromFormOnPage

    Returns all records from the Form with the id = formId on the Umbraco page with the id = pageId as a PagedResult<Record>.

    hashtag
    GetRecordsFromForm

    Returns all records from the Form with the ID = formId as a PagedResult<Record>.

    hashtag
    The returned objects

    All of these methods will return an object of type PagedResult<Record> so you can iterate through the Record objects.

    The properties available on a Record are:

    In order to access custom Form fields, these are available in the RecordFields property. Furthermore there exists an extension method named ValueAsString on Record in Umbraco.Forms.Core.Extensions, such that you can get the value as string given the alias of the field.

    This extension method handle multi value fields by comma separating the values. E.g. "A, B, C"

    hashtag
    Sample razor script

    Sample script that is outputting comments using a Form created with the default comment Form template.

    hashtag
    Loading a Record From a Submitted Form

    When a form is submitted, the submitted form ID and the saved record ID are stored in the TempData so they can be referenced.

    You can use the FormService and the RecordStorage to get the Form and Record objects.

    Here is a sample code for retrieving a record in a view.

    RecordField? recordField = context.Record.GetRecordFieldByAlias("myalias");
    var fieldValue = recordField.ValuesAsString(false);
    IEnumerable<string> selectedPrevalues = recordField.GetSelectedPrevalues();
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Cms.Core.DependencyInjection;
    using Umbraco.Forms.Core.Providers;
    
    namespace MyFormsExtensions
    {
        public class Startup : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.WithCollectionBuilder<WorkflowCollectionBuilder>()
                    .Add<TestWorkflow>();
            }
        }
    }
    @if (Context.Items.TryGetValue("UmbracoForms", out object? formIdsObject) && formIdsObject is IEnumerable<Guid> formIds)
    {
        foreach (var formId in formIds)
        {
            @await Component.InvokeAsync("RenderFormScripts", new { formId, theme = "default" })
        }
    }
    @using Umbraco.Forms.Web.Extensions;
    
    @if (TempData.Get<Guid[]>("UmbracoForms") is Guid[] formIds)
    {
        foreach (var formId in formIds)
        {
            @await Component.InvokeAsync("RenderFormScripts", new { formId, theme = "default" })
        }
    
        TempData.Remove("UmbracoForms");
    }
    @addTagHelper *, Umbraco.Forms.Web
    <umb-forms-render-scripts theme="default" />
    <head>
        @Html.RenderUmbracoFormDependencies(Url)
    </head>
    <body>
        @await Component.InvokeAsync("RenderForm", new { formId = Model.FormGuid })
        <umb-forms-render-scripts theme="default" />
    </body>
    @await Umbraco.RenderMacroAsync("renderUmbracoForm", new {FormGuid="6c3f053c-1774-43fa-ad95-710a01d9cd12", FormTheme="bootstrap3-horizontal", ExcludeScripts="1"})
    /*
     Reverts application of recommended primary keys, foreign keys and indexes to Umbraco Forms tables relating to "forms in the database" (i.e.
     when configuration key StoreUmbracoFormsInDb = true).
     This reverts for SQL Server the migration AddFormKeysAndIndexes and can be used for rolling that back in testing.
     */
    
    -- Reverts addition of unique constraint to UFForms.
    ALTER TABLE dbo.UFForms
    DROP CONSTRAINT IF EXISTS UK_UFForms_Key
    GO
    
    -- Reverts addition of unique constraint to UFPrevalueSource.
    ALTER TABLE dbo.UFDataSource
    DROP CONSTRAINT IF EXISTS UK_UFDataSource_Key
    GO
    
    -- Reverts addition of unique constraint to UFPrevalueSource.
    ALTER TABLE dbo.UFPrevalueSource
    DROP CONSTRAINT IF EXISTS UK_UFPrevalueSource_Key
    GO
    
    -- Reverts addition of unique constraint to UFWorkflows.
    ALTER TABLE dbo.UFWorkflows
    DROP CONSTRAINT IF EXISTS UK_UFWorkflows_Key
    GO
    
    -- Reverts addition of index on foreign key fields in UFWorkflows.
    DROP INDEX IF EXISTS IX_UFWorkflows_FormId ON dbo.UFWorkflows
    GO
    
    -- Reverts addition of index on foreign key fields in UFWorkflows.
    DROP INDEX IF EXISTS IX_UFWorkflows_FormId ON dbo.UFWorkflows
    GO
    PagedResult<Record> GetApprovedRecordsFromPage(int pageId, int pageNumber, int pageSize)
    PagedResult<Record> GetApprovedRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize)
    PagedResult<Record> GetApprovedRecordsFromForm(Guid formId, int pageNumber, int pageSize)
    PagedResult<Record> GetRecordsFromPage(int pageId, int pageNumber, int pageSize)
    PagedResult<Record> GetRecordsFromFormOnPage(int pageId, Guid formId, int pageNumber, int pageSize)
    PagedResult<Record> GetRecordsFromForm(Guid formId, int pageNumber, int pageSize)
    int Id
    FormState State
    DateTime Created
    DateTime Updated
    Guid Form
    string IP
    int UmbracoPageId
    string MemberKey
    Guid UniqueId
    Dictionary<Guid, RecordField> RecordFields
    @using Umbraco.Core;
    @using Umbraco.Cms.Core.Composing;
    @using Umbraco.Forms.Core.Extensions;
    @inject IRecordReaderService _recordReaderService;
    
    <ul id="comments">
        @foreach (var record in _recordReaderService.GetApprovedRecordsFromPage(Model.Id, 1, 10).Items)
        {
        <li>
            @record.Created.ToString("dd MMMM yyy")
            @if(string.IsNullOrEmpty(record.ValueAsString("email"))){
                <strong>@record.ValueAsString("name")</strong>
            }
            else{
                <strong>
                    <a href="mailto:@record.ValueAsString("email")" target="_blank">@record.ValueAsString("name")</a>
                </strong>
            }
            <span>said</span>
            <p>@record.ValueAsString("comment")</p>
        </li>
        }
    </ul>
    @using Umbraco.Forms.Core.Models
    @using Umbraco.Forms.Core.Persistence.Dtos
    @using Umbraco.Forms.Core.Data.Storage
    @using Umbraco.Forms.Core.Services
    @inject IFormService _formService
    @inject IRecordStorage _recordStorage
    @inherits UmbracoViewPage
    @{
    	Guid formId;
    	Form? form;
    	Guid recordId;
    	Record? record;
        string submittedEmail;
    
    	if (Guid.TryParse(TempData["UmbracoFormSubmitted"]?.ToString(), out Guid formId) &&
    	    Guid.TryParse(TempData["Forms_Current_Record_id"]?.ToString(), out Guid recordId))
    	{
    
    		form = _formService.Get(formId);
    
    		if (form != null && TempData["Forms_Current_Record_id"] != null)
    		{
    			Guid.TryParse(TempData["Forms_Current_Record_id"]?.ToString(), out recordId);
    
    			record = _recordStorage.GetRecordByUniqueId(recordId, form);
    
                submittedEmail = record.GetRecordFieldByAlias("email")?.ValuesAsString();
    		}
    	}
    }
    The Choose prevalue source type pane opens in the right-side of the editor.
  • Select the type of prevalue source. For more information on the different default types, see the Overview of the Prevalue Source Types article.

  • Select Click to upload in the Text File.

  • Choose the text file you created. Click Open.

  • Select your preferred Cache option for caching the list of prevalues when rendering in a form.

  • Click Save.

    Prevalue source settings
  • - the list will be cached on first request and not retrieved again until either the prevalue source is edited or the website is restarted. This ismost appropriate to use for information held within the prevalue source data itself (such as when uploading a text file).
    Prevalue cache options
    Prevalue source
    Preview
    Type settings
    The behavior to display a message to the user who submitted the form can be configured by clicking on the built-in first workflow step. This step is labelled Submit message/Go to page, and it can also configure the redirection to another page.
    Submit message/Go to page

    If a value is selected for Go to page, it will be used to redirect to that page once the form has been submitted.

    If no value is selected, the message in Message on submit is displayed to the user on the same page, instead of the form fields. This is implemented via a redirect to the current page, ensuring that the form can't be accidentally resubmitted.

    By default, the message is created and rendered in plain text. If you need to add formatting to the message, toggle the Format message in rich text button.

    Submit message/Go to page

    hashtag
    Video Tutorial

    hashtag
    Adding a Workflow

    At the bottom of your Form, a default workflow is already attached to the Form, as well as an option to configure the workflows.

    Button

    Clicking Configure workflow will give you the option to configure existing workflows, as well as setup new ones.

    Workflow add

    hashtag
    Choose a Workflow

    A new workflow can be of different types and Umbraco Forms ships with a few default ones. You can find an overview of the types in the Workflow types article.

    Workflow add modal

    hashtag
    Update Type-specific Settings

    Once the Workflow Type has been selected, you will need to configure the workflow. There are different settings depending on the type that has been selected.

    To use data from the submitted Form in your workflow, head over to the Magic Strings article and learn more about how that's done.

    hashtag
    Configuring Condition on a Workflow

    You can apply conditions to a workflow that trigger it only under specific circumstances. After adding the desired workflow type (for example, sending an email), you can add a condition to the workflow.

    Select Enable conditions to open the condition editor. In the condition editor, you will see options to create logic that determines when the workflow should run. The condition is generally based on the values of the form fields.

    For example: You have a form with a dropdown field labeled Preferred Contact Method with options such as Email and Phone. You can set up a workflow that sends an email notification only when the user selects Email.

    Workflow Conditions

    Now, this email notification will only be sent when the user selects Email as their preferred contact method.

    Fill in the rest of the settings and click Submit. The workflow is added to your Form and displayed at the bottom of the page.

    hashtag
    Workflow Processing

    When a form is submitted, any workflows associated with the "submit" stage of the form will run sequentially in the configured order. The record is stored after these workflows are completed, and as such they can make changes to the information recorded.

    Similarly, approval of a form entry, whether automatic or manual, will trigger the execution of the workflows associated with the "approve" stage.

    Rejection of an entry will trigger the execution of the workflows associated with the "reject" stage.

    If a workflow encounters an unexpected error, it will silently fail from the perspective of the user submitting the form. The exception along with the other details of the failed operation is recorded to the log.

    From Umbraco Forms versions 8.13.0 and 10.1, an audit trail has been made available. In the list of entries for a form, a summary is presented that shows how many workflows were executed, and how many were successful:

    Workflow execution summary

    For each entry, in the backoffice a table can be viewed that shows each of the workflows and the success, or otherwise, of the operation.

    Workflow execution summary

    For any workflows that did not complete successfully, a "Retry" link is available to trigger the workflow again. This is useful for example if there was a temporary infrastructure issue that perhaps prevented an email going out. You would be able to retrigger the workflow once the issue is resolved.

    Store records

    Themes

    Documentation on how to apply custom themes to Umbraco Forms

    Umbraco Forms supports Themes, allowing forms to be customized in a much simpler manner.

    hashtag
    Creating a Theme

    To create a theme, you need to create a folder at /Views/Partials/Forms/Themes/. The name of the folder is the name of theme that will be visible in the backoffice when choosing it.

    Copy the explicit files you wish to override in your theme, it may be a single file or all files from the default theme folder. Make the necessary changes you desire to CSS class names, markup etc.

    hashtag
    Obtaining the Default Theme Files

    For Umbraco 9 and previous, it's straightforward to copy the files you need from the default theme folder. We highly recommend that you never customize any files found in the default themes folder. There is a risk that any customizations to these files will be lost with any future upgrades you do to Umbraco Forms. Umbraco 10+ distributes these files as part of a Razor Class Library, so you won't find them on disk. Instead you should download the appropriate zip file for your Forms version and extract the ones you need.

    You can obtain the latest version of the Forms default theme from the following links:

    You should use the theme available for the highest version that's less or equal to the version of Forms you have installed.

    hashtag
    Amending Theme Files

    circle-info

    Umbraco Forms conditional JavaScript logic depends on some CSS classes currently and it is advised that you add any additional classes you require but do not remove those already being set.

    If adding or amending client-side scripts, you need to copy the Script.cshtml file from the default themes folder. In your copy, amend the .js references to reference your own script files.

    hashtag
    Shipping Themes in a Razor Class Library

    Umbraco Forms provides it's built-in themes as part of a Razor Class Library for ease of distribution. This can be useful for custom themes, particularly those used in multiple solutions or released as an Umbraco package.

    It is also possible to do this for custom themes.

    1. Create a new Razor Class Library project to hold the theme.

    2. Create the necessary Partial Views for your theme within Views\Partials\Forms\Themes\<my-custom-theme>.

    3. Provide the names of the files in your theme via an implementation of ITheme.

    • For example, if only overriding a single file, your class would look like the code snippet below:

    1. Register the themes you want to use via a composer:

    Your theme will now be available in the Theme Picker and the partial view files will be used when rendering forms.

    hashtag
    Email Templates

    Email templates provided for the send email workflow can be provided in a Razor Class Library similar to the Theme files.

    The partial view will be created in Views\Partials\Forms\Emails.

    It's made available via an implementation of IEmailTemplate:

    And registered with:

    Removing the Default Email Template

    If providing custom email templates, you may want to remove the one provided with Forms. You can do that via the same EmailTemplates collection.

    hashtag
    Using a Theme

    When rendering a form in a view file, you can specify which theme to use with the form.

    Learn more about how to render a form with a theme in the article.

    hashtag
    Theme Fallbacks

    When using a theme, Umbraco Forms will try to use a view from the theme folder, but then fallback to the same view in the default theme folder if it can't be found. This allows you to create a theme by only modifying the files necessary to make your customizations.

    Files which can be overridden:

    • Render.cshtml (overrides the entire form - usually not needed)

    • Form.cshtml (overrides the generation of the fields on the current page)

    • Script.cshtml (overrides the way files are included with the form)

    hashtag
    Helper Methods

    hashtag
    SetFormThemeCssFile

    Sets the primary form theme stylesheet path. This overrides an already assigned stylesheet and will be rendered out when inserting the form into the page

    hashtag
    AddFormThemeScriptFile

    Add a JavaScript file path to include on form render

    hashtag
    SetFormFieldClass

    Adds a class to the form field HTML element of a given type. If no type is given, it will add the class to all fields

    hashtag
    GetFormFieldClass

    Retrieves all classes for a given field type, used when rendering form fieldtype partial views

    hashtag
    SetFormFieldWrapperClass

    Adds a class to the div element wrapping around form fields of a given type. If no type is given, it will add the class to all fields

    hashtag
    GetFormFieldWrapperClass

    Retrieves all wrapper classes for a given field type, used when rendering form fields. This class wraps both label, help-text and the field itself in the default view

    Form Settings

    In this article, you will find information about accessing the Form Settings and the options available to customize your Form.

    To access the Form Settings:

    1. Go to the Forms section.

    2. Open a Form you wish to customize.

    3. Click Settings in the top-right corner of the screen.

    hashtag
    Settings Options

    The following options are available in Forms Settings:

    hashtag
    Store Records

    By default, all submitted records are saved in the database. This option allows you to view and export the saved records from the queries overview. If you do not want to store data (due to policies in your organization), you can uncheck the box.

    Disabling this option will prevent database records from being stored, but any file uploads made as part of the form submission will still be retained. If you do not want the files to be stored, ensure that any process or method used to process, move, or copy them to a different location also removes the file.

    hashtag
    Captions

    Customize the labels of the Submit, Next, and Previous buttons used in your Form.

    hashtag
    Styling

    Set a stylesheet to give your Form custom styling. You have an option to disable the default styling. Enabling the Disable default stylesheet option will prevent a default stylesheet to be added to the pages where the Form is placed.

    hashtag
    Validation

    Define a message that is displayed when a field is mandatory, when a value is not supplied, or when the value is invalid.

    The following Validations are available:

    Validation Type
    Description

    hashtag
    Autocomplete

    The autocomplete setting for the overall form can be changed from the default of "None" to "On" or "Off". Setting this explicitly will control how the browser offers automatic prompts to the user when completing the form.

    hashtag
    Multi-page forms

    The settings available in this section allow you to customize how multi-page forms are presented to site visitors.

    Option
    Description
    circle-info

    These options will only be available if .

    hashtag
    Moderation

    Enabling this feature allows the moderator to manage the approval status of a form. This can be used in a number of scenarios. For example, if the form submission will be publicly shown, you can control which are published.

    hashtag
    Fields Displayed

    By default, a constant set of fields are displayed when form entries are shown in a list. You will see the first three fields in the form, plus some system information like the record state and the date it was created.

    To customize this, turn off the "Display default fields" option and select the ones you wish to display.

    hashtag
    Data Retentions

    To help protect site visitor privacy, rules can be configured in this section for the automatic deletion of submissions. You can set how long to retain records for each state (submitted, approved or rejected).

    A background service that carries out the actual removal of records needs to be . If that is not running, a notification will be displayed.

    Migration IDs

    A unique migration ID is generated for each Umbraco Forms upgrade that requires a migration. The migration IDs are all listed in this article.

    Migration ID
    Introduced In Version
    Description

    7c7bc5ee-4c5b-42dc-9576-5ce6dfbddb8e

    10.0.0

    Installs Umbraco Forms.

    Creating a Form - The basics

    This article walks you through creating a basic form in Umbraco Forms and adding it to your site. You’ll learn how to access the Forms section, build a form with pages, groups, and fields, and publish it on a content page.

    hashtag
    Accessing the Forms Section

    You can manage the Forms in the Forms section of the Umbraco backoffice. You need access to the section to see it.

    If you cannot see the Forms section, ask an Administrator to grant you access from the Users section.

    Extending

    Umbraco Forms functionality can be extended in different ways.

    For front-end extensions, specifically via theming, see the section.

    hashtag
    Extending the Backoffice

    Umbraco Forms publishes an NPM package called @umbraco-forms/backoffice that holds typings and other niceties to build extensions.

    Health Checks

    In this article, you will find information about Umbraco Forms-related health checks that can be run from the Umbraco backoffice to ensure that your installation is running seamlessly.

    Read the article to learn more about the feature in general.

    hashtag
    Database Integrity Health Check

    Running this health check will verify whether the database tables for the Umbraco Forms installation are all set up correctly with the proper data integrity checks.

    example value 1
    example value 2
    example value 3
    example value 4
    example value 5
    1|example value 1
    2|example value 2
    3|example value 3
    4|example value 4
    5|example value 5

    9f7e6fe6-bbd5-4b2b-8820-e9e0e36cc74c

    10.1.0

    Adds Culture column to Records table.

    1a8f0d04-9396-40a2-9423-39fc9ae3828f

    10.1.0

    Adds a Record Workflow Audit table.

    6e692c5d-c670-4c34-af17-28d8dbf0dcd2

    10.1.0

    Adds an ExecutionStage column to the Record Workflow Audit table.

    5d84fee1-388c-4e5f-b98c-1e66947278f1

    10.1.0

    No operation migration.

    22df962a-ae26-4bdd-b8fd-0513a9c636bf

    10.5.2/12.1.2

    Ensures the presence of an index on the FolderKey column in the Forms table.

    c3e657f6-3ae7-4ee9-b442-01702a41de9a

    12.2.0/13.0.0

    Adds a relation between content and forms.

    e0290a40-91c9-4acb-a7ca-d312037078f2

    12.2.0/13.0.0

    Adds a NodeId column to Forms table

    6f0eb771-6690-4b53-870a-f7dbb2785cac

    12.2.0/13.0.0

    Populates the NodeId column in the Forms table.

    44949e12-e4ef-42c0-949b-67286b946fe0

    12.2.0/13.0.0

    No operation migration.

    773ae769-00b7-4429-b7d5-de0fda0b4217

    12.2.1/13.0.1

    Ensures the consistent key is used for the relation type between content and forms.

    55d53d2e-f795-42fb-9e77-8edfc6eed4aa

    13.2.0

    Adds an AdditionalData column to the Records table.

    1fff8b7b-48e7-450a-80b1-7df628508b27

    13.3.0

    Adds delete entries permissions field to the security tables.

    7e170195-cab7-48ca-98c7-bbcbd5cfda95

    13.4.0

    Adds created and updated by columns to the entity tables.

    c74223ed-a554-4a14-a1f0-0477dce01ad6

    14.0.0

    Updates the form picker property editor UI alias.

    a5ffa9a7-ca77-4a7c-a1e4-f32e25cde758

    14.1.0/15.0.0

    Same as 13.2.0 to allow upgrading 14.1 to 15.0.

    db5ef50d-51d0-4f93-aae9-bd3df53a3bb1

    14.2.0/15.0.0

    Same as 13.3.0 to allow upgrading 14.2 to 15.0.

    5b74ad79-3faa-4c08-bfba-472a860704e5

    13.4.0/15.1.0/16.0.0

    Same as 13.4.0 to allow upgrading 13.4/15.1 to 16.0.

    8f3c2d7e-6a1b-4e9f-9c8a-2d4e5f6a7b8c

    16.4.0

    Adds FieldPreValueSourceTypeId column to field pre-values.

    0f296610-47f5-415d-8042-f4b8f2a51a4a

    16.4.0

    Migrates node pre-values to dynamic root.

    d3c1e2f4-5b6a-4c7d-8e9f-0a1b2c3d4e5f

    16.4.0

    Updates the form picker property editor UI alias.

    59576376-b235-47a1-b495-1f795316ee04

    17.0.0

    Migrates system dates to UTC.

    5e608c91-e910-42a5-8302-928b580ffd54

    17.0.0

    Migrates node pre-values to dynamic root.

    67c4820d-d802-44dd-b146-5a1ba4c2b5f8

    17.1.0

    Adds FieldPreValueSourceTypeId column to field pre-values.

    b375063a-8fcb-4096-b6e2-ff32ff322677

    17.1.0

    Migrates node pre-values to dynamic root.

    3f4e5d6c-7b8a-4c9d-0e1f-2a3b4c5d6e7f

    17.1.0

    Updates the form picker property editor UI alias.

    6a094cba-aa2c-4254-aaff-ced3d09eccf3

    17.3.0

    Adds pre-aggregated analytics tables.

    a7b3c9d2-4e5f-6a1b-8c7d-9e0f1a2b3c4d

    17.3.0

    Adds an index on the Record table for form and created date.

    Watch this video to learn how to manage entries submitted via Umbraco Forms.

    Mark fields

    You can choose to not mark any fields or only mark mandatory or optional fields.

    Indicator

    Choose which indicator to use when a field has been marked as mandatory. The default indicator is *

    Summary heading

    Provide the heading for the summary page.

    Mandatory error message

    The error message is displayed for a field that is marked as mandatory but a value has not been provided upon submission. This setting can be overwritten on a field level - {0} will be replaced with the field caption.

    Invalid error message

    The error message is displayed for a field if the value provided is not valid (a regular expression has been setup but the input does not match). This setting can be overwritten on a field level - {0} will be replaced with the field caption.

    Show validation summary

    Enable this option if you wish to display a summary of all the error messages on top of the Form.

    Hide field validation labels

    Paging display

    Select whether paging information is displayed at the top and/or bottom of the form.

    Paging display format

    Provide a format string for the paging details. By default Page {0} of {1} is used which will be replaced as, for example, Page 1 of 4.

    Page caption format

    Provide a format string for rendering the page captions. By default Page {0} is used which will be replaced as, for example, Page 1. If a caption for the page has been provided, it will be used instead.

    Show summary page

    the feature is configured for display
    enabled in configuration
    Form settings dialog
    Form settings Store Records
    Form settings stylesheet
    Form settings stylesheet
    Form settings validation
    Form Settings Autocomplete
    Multi-Page Form Settings
    Form settings Moderation
    Form settings Fields Displayed
    Form settings Date Retentions

    Enable this option if you wish to hide individual field error messages from being displayed.

    Select whether a summary page is displayed at the end of multi-page forms, where a user can review their entry before submitting.

    17.2.0arrow-up-right
    /Fieldtypes/FieldType.*.cshtml (overrides a specific view for a field)
    17.0.0arrow-up-right
    17.0.2arrow-up-right
    17.1.0arrow-up-right
    Rendering Forms

    Apply keys and indexes

    hashtag
    Revert application of keys and indexes

    /*
     Reverts application of recommended primary keys, foreign keys and indexes to core Umbraco Forms tables.
     This reverts for SQL Server the migration AddRecordKeysAndIndexes and can be used for rolling that back in testing.
     */
    
    -- Reverts addition of relationship between UFRecords and UFRecordFields.
    ALTER TABLE dbo.UFRecordFields
    DROP CONSTRAINT IF EXISTS FK_UFRecordFields_UFRecords_Record
    GO
    
    -- Reverts addition of primary keys to UFRecordData* tables.
    ALTER TABLE dbo.UFRecordDataBit
    DROP CONSTRAINT IF EXISTS PK_UFRecordDataBit
    GO
    
    ALTER TABLE dbo.UFRecordDataDateTime
    DROP CONSTRAINT IF EXISTS PK_UFRecordDataDateTime
    GO
    
    ALTER TABLE dbo.UFRecordDataInteger
    DROP CONSTRAINT IF EXISTS PK_UFRecordDataInteger
    GO
    
    ALTER TABLE dbo.UFRecordDataLongString
    DROP CONSTRAINT IF EXISTS PK_UFRecordDataLongString
    GO
    
    -- Reverts addition of relationship between UFRecordFields and UFREcordData* tables.
    ALTER TABLE dbo.UFRecordDataBit
    DROP CONSTRAINT IF EXISTS FK_UFRecordDataBit_UFRecordFields_Key
    GO
    
    ALTER TABLE dbo.UFRecordDataDateTime
    DROP CONSTRAINT IF EXISTS FK_UFRecordDataDateTime_UFRecordFields_Key
    GO
    
    ALTER TABLE dbo.UFRecordDataInteger
    DROP CONSTRAINT IF EXISTS FK_UFRecordDataInteger_UFRecordFields_Key
    GO
    
    ALTER TABLE dbo.UFRecordDataLongString
    DROP CONSTRAINT IF EXISTS FK_UFRecordDataLongString_UFRecordFields_Key
    GO
    
    -- Reverts addition of index on foreign key fields in UFREcordData* tables.
    DROP INDEX IF EXISTS IX_UFRecordDataBit_Key ON dbo.UFRecordDataBit
    GO
    
    DROP INDEX IF EXISTS IX_UFRecordDataDateTime_Key ON dbo.UFRecordDataDateTime
    GO
    
    DROP INDEX IF EXISTS IX_UFRecordDataInteger_Key ON dbo.UFRecordDataInteger
    GO
    
    DROP INDEX IF EXISTS IX_UFRecordDataLongString_Key ON dbo.UFRecordDataLongString
    GO
    
    -- Reverts addition of primary key to UFUserSecurity
    ALTER TABLE dbo.UFUserSecurity
    DROP CONSTRAINT IF EXISTS PK_UFUserSecurity
    GO
    
    -- Reverts addition of primary key to UFUserFormSecurity
    ALTER TABLE dbo.UFUserFormSecurity
    DROP CONSTRAINT IF EXISTS PK_UFUserFormSecurity
    GO
    
    -- Reverts addition of unique constraint to UFUserFormSecurity across user/form fields.
    ALTER TABLE dbo.UFUserFormSecurity
    DROP CONSTRAINT IF EXISTS UK_UFUserFormSecurity_User_Form
    GO
    circle-exclamation

    Ensure that you install the version of the Backoffice package that is compatible with your Umbraco Forms installation. You can find the appropriate version on the @umbraco-forms/backoffice npm pagearrow-up-right.

    You can install this package by running the command:

    This will add a package to your devDependencies containing the TypeScript definitions for Umbraco Forms.

    hashtag
    TSConfig

    Make sure to configure your TypeScript compiler so it includes the Global Types from the package. This enables you to utilize the declared Extension Types. If your project uses other Packages that provide their Extension Types, list those as well.

    In your tsconfig.json file, add the array types inside compilerOptions, with the entry of @umbraco-forms/backoffice:

    hashtag
    Take extra care when using Vite

    It is important that this namespace is ignored in your bundler. If you are using Vite, you can add the following to your vite.config.ts file:

    This ensures that the Umbraco Backoffice packages are not bundled with your package.

    Read more about using Vite with Umbraco in the Vite Package Setuparrow-up-right article.

    hashtag
    Developing Custom Providers

    Although the Forms package comes with many fields, workflows and other built-in types, you can still create and develop your own if needed.

    hashtag
    Provider model

    Many features of Forms use a provider model, which makes it quicker to add new parts to the application.

    The model uses the notion that everything must have a type to exist. The type defines the capabilities of the item. For instance a Textfield on a form has a FieldType, this particular field type enables it to render an input field and save text strings. The same goes for workflows, which have a workflow type, datasources which have a datasource type and so on. Using the model you can seamlessly add new types and thereby extend the application.

    It is possible to add new Field types, Data Source Types, Prevalue Source Types, Export Types, and Workflow Types.

    hashtag
    Field types

    A field type handles rendering of the UI for a field in a form. It renders a standard ASP.NET Razor partial view and is able to return a list of values when the form is saved.

    The concept of provider settings, common to the field and other types, is also discussed in this section.

    hashtag
    Data Source Types

    A data source type enables Umbraco Forms to connect to a custom source of data. A data source consists of any kind of storage if it is possible to return a list of fields Umbraco Forms can map values to. For example: a Database data source can return a list of columns Forms can send data to. This enables Umbraco Forms to map a form to a data source. A data source type is responsible for connecting Forms to external storage.

    hashtag
    Prevalue Source Types

    A prevalue source type connects to 3rd party storage to retrieve values. These values are used on fields supporting prevalues. The source fetches the collection of values.

    hashtag
    Workflow Types

    A workflow can be executed each time a form changes state (when it is submitted for instance). A workflow is responsible for executing logic which can modify the record or notify 3rd party systems.

    hashtag
    Export Types

    Export types are responsible for turning form records into any other data format, which is then returned as a file.

    hashtag
    Magic String Format Functions

    Custom magic string format functions to add to the ones shipped with Umbraco Forms can be created in code.

    hashtag
    Validation Patterns

    When creating a text field in Umbraco Forms, a validation pattern in the form of a regular expression can be applied. Default patterns can be removed or re-ordered, and custom ones created and added.

    hashtag
    Handling Forms Events

    Another option for extension via custom code is to hook into one of the many events available.

    hashtag
    Validation

    Form events are raised during the submission life cycle and can be handled for executing custom logic.

    hashtag
    Default Fields and Workflows

    When a new form is created, the default behavior is to add a single workflow. This workflow will send a copy of the form to the current backoffice user's email address.

    A single "data consent" field will also be added unless it has been disabled via configuration.

    It's possible to amend this behavior and change it to fit your needs.

    hashtag
    Responding to State Values

    In the course of submitting a form, Umbraco Forms will set values in TempData and/or HttpContext.Items, that you can use to customize the website functionality.

    hashtag
    Customizing Post-Submission Behavior

    Whether displaying a message or redirecting, a developer can customize the page viewed after the form is submitted based on the presence of TempData variables.

    One variable with a key of UmbracoFormSubmitted has a value containing the Guid identifier for the submitted form.

    A second variable contains the Guid identifier of the record created from the form submission. You can find this using the Forms_Current_Record_id key.

    In order to redirect to an external URL rather than a selected page on the Umbraco website, you will need to use a custom workflow. Within this workflow you can set the required redirect URL on the HttpContext.Items dictionary using the key FormsRedirectAfterFormSubmitUrl (defined in the constant Umbraco.Forms.Core.Constants.ItemKeys.RedirectAfterFormSubmitUrl).

    For example, using an injected instance of IHttpContextAccessor:

    Themes
    using Umbraco.Forms.Core.Interfaces;
    
    public class MyCustomTheme : ITheme
    {
        private const string FilePathFormat = "{0}/{1}/{2}.cshtml";
    
        public virtual string Name => "my-custom-theme";
    
        public virtual IEnumerable<string> Files =>
            [
                string.Format(FilePathFormat, Core.Constants.System.ThemesPath, Name, "FieldTypes/FieldType.Textfield"),
            ];
    }
    public class MyComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
          builder.Themes()
              .Add<MyCustomTheme>();
        }
    }
    using Umbraco.Forms.Core.Interfaces;
    
    public class MyCustomEmailTemplate : IEmailTemplate
    {
        public virtual string FileName => "My-Custom-Email-Template.cshtml";
    }
    public class MyComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
          builder.EmailTemplates()
              .Add<MyCustomEmailTemplate>();
        }
    }
    public class MyComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
          builder.EmailTemplates()
              .Exclude<DefaultEmailTemplate>();
        }
    }
    Html.SetFormThemeCssFile(Model, "~/App_Plugins/UmbracoForms/Assets/Themes/Default/style.css")
    Html.AddFormThemeScriptFile("~/App_Plugins/UmbracoForms/Assets/themes/default/umbracoforms.js");
    // Applies the CSS class 'form-control' to all fields that GetFormFieldClass uses in FieldType views
    @Html.SetFormFieldClass("form-control")
    
    // Applies the CSS class 'some-other-class' for the FieldType of the name 'Password'
    @Html.SetFormFieldClass("some-other-class", "Password")
    class="@Html.GetFormFieldClass(Model.FieldTypeName)"
    // Applies the CSS class 'form-group' around all fields, labels & help texts
    @Html.SetFormFieldWrapperClass("form-group")
    
    // Applies the CSS class 'some-other-class' for the FieldType of the name 'Password'
    @Html.SetFormFieldWrapperClass("some-other-class", "Password")
    class="@Html.GetFormFieldWrapperClass(f.FieldTypeName)"
    /*
     Applies recommended primary keys, foreign keys and indexes to core Umbraco Forms tables.
     This replicates for SQL Server the migration AddRecordKeysAndIndexes.
     */
    
    -- Adds relationship between UFRecords and UFRecordFields.
    ALTER TABLE dbo.UFRecordFields
    ADD CONSTRAINT
    	FK_UFRecordFields_UFRecords_Record FOREIGN KEY
    	(
    	Record
    	) REFERENCES dbo.UFRecords
    	(
    	Id
    	) ON UPDATE  NO ACTION 
    	 ON DELETE  NO ACTION 
    GO
    
    -- Adds primary keys to UFRecordData* tables.
    ALTER TABLE dbo.UFRecordDataBit
    ADD CONSTRAINT
    	PK_UFRecordDataBit PRIMARY KEY CLUSTERED 
    	(
    	Id
    	) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    ALTER TABLE dbo.UFRecordDataDateTime
    ADD CONSTRAINT
    	PK_UFRecordDataDateTime PRIMARY KEY CLUSTERED 
    	(
    	Id
    	) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    ALTER TABLE dbo.UFRecordDataInteger
    ADD CONSTRAINT
    	PK_UFRecordDataInteger PRIMARY KEY CLUSTERED 
    	(
    	Id
    	) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    ALTER TABLE dbo.UFRecordDataLongString
    ADD CONSTRAINT
    	PK_UFRecordDataLongString PRIMARY KEY CLUSTERED 
    	(
    	Id
    	) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    -- Adds relationship between UFRecordFields and UFREcordData* tables.
    ALTER TABLE dbo.UFRecordDataBit
    ADD CONSTRAINT
    	FK_UFRecordDataBit_UFRecordFields_Key FOREIGN KEY
    	(
    	[Key]
    	) REFERENCES dbo.UFRecordFields
    	(
    	[Key]
    	) ON UPDATE  NO ACTION 
    	 ON DELETE  NO ACTION 
    GO
    
    ALTER TABLE dbo.UFRecordDataDateTime
    ADD CONSTRAINT
    	FK_UFRecordDataDateTime_UFRecordFields_Key FOREIGN KEY
    	(
    	[Key]
    	) REFERENCES dbo.UFRecordFields
    	(
    	[Key]
    	) ON UPDATE  NO ACTION 
    	 ON DELETE  NO ACTION 
    GO
    
    ALTER TABLE dbo.UFRecordDataInteger
    ADD CONSTRAINT
    	FK_UFRecordDataInteger_UFRecordFields_Key FOREIGN KEY
    	(
    	[Key]
    	) REFERENCES dbo.UFRecordFields
    	(
    	[Key]
    	) ON UPDATE  NO ACTION 
    	 ON DELETE  NO ACTION 
    GO
    
    ALTER TABLE dbo.UFRecordDataLongString
    ADD CONSTRAINT
    	FK_UFRecordDataLongString_UFRecordFields_Key FOREIGN KEY
    	(
    	[Key]
    	) REFERENCES dbo.UFRecordFields
    	(
    	[Key]
    	) ON UPDATE  NO ACTION 
    	 ON DELETE  NO ACTION 
    GO
    
    -- Adds index on foreign key fields in UFREcordData* tables.
    CREATE NONCLUSTERED INDEX IX_UFRecordDataBit_Key ON dbo.UFRecordDataBit
    (
    	[Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    CREATE NONCLUSTERED INDEX IX_UFRecordDataDateTime_Key ON dbo.UFRecordDataDateTime
    (
    	[Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    CREATE NONCLUSTERED INDEX IX_UFRecordDataInteger_Key ON dbo.UFRecordDataInteger
    (
    	[Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    CREATE NONCLUSTERED INDEX IX_UFRecordDataLongString_Key ON dbo.UFRecordDataLongString
    (
    	[Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    -- Adds primary key to UFUserSecurity.
    ALTER TABLE dbo.UFUserSecurity
    ADD CONSTRAINT
    	PK_UFUserSecurity PRIMARY KEY CLUSTERED 
    	(
    	[User]
    	) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    -- Adds primary key to UFUserFormSecurity.
    ALTER TABLE dbo.UFUserFormSecurity
    ADD CONSTRAINT
    	PK_UFUserFormSecurity PRIMARY KEY CLUSTERED 
    	(
    	Id
    	) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    
    -- Adds unique constraint to UFUserFormSecurity across user/form fields.
    ALTER TABLE dbo.UFUserFormSecurity
    ADD CONSTRAINT UK_UFUserFormSecurity_User_Form UNIQUE NONCLUSTERED 
    (
    	[User] ASC,
    	[Form] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    npm install -D @umbraco-forms/[email protected]
    {
        "compilerOptions": {
            ...
            "types": [
                ...
                "@umbraco-forms/backoffice"
            ]
        }
    }
    import { defineConfig } from "vite";
    
    export default defineConfig({
        // other config
        // ...
        // add this to your config
        build: {
            rollupOptions: {
                external: [/^@umbraco/],
            },
        }
    });
    _httpContextAccessor.HttpContext.Items[Constants.ItemKeys.RedirectAfterFormSubmitUrl] = "https://www.umbraco.com";
    Forms Section

    hashtag
    Creating a new Form

    To create a Form, follow these steps:

    1. Go to the Forms section in the backoffice.

    2. Click ... next to the Forms folder.

    3. Select Create > New Form. The Form Designer opens in the editor.

    4. Enter a Name for the Form. For example, Our first form.

    5. Click Add new page.

    6. [Optional] Enter a Page Name. For example, The first page.

    7. [Optional] Enter a Group Name. For example, General Information Group.

    8. Click Add Question.

    9. Select Short Answer in the Choose field type dialog.

    10. Enter the following values in the Edit field window:

    Field Name
    Value

    Enter a name

    Name

    Enter help text

    Enter your name here

    1. Click Submit to add the field to the form.

    2. Click Save.

    Similary, you can add other fields to the form.

    Create new Form

    hashtag
    Editing a Field

    To edit a field:

    1. Click the cog icon next to the field.

    2. Edit the desired field.

    3. Click Submit.

    hashtag
    Copying a Field

    Click the Copy icon to duplicate the field and all its settings. The copied field is added below the original.

    hashtag
    Deleting a Field

    To delete a field or a group:

    1. Go to the field or group you wish to delete.

    2. Click the Trash icon.

    3. Click Delete.

    hashtag
    Structuring the Form

    hashtag
    Ordering Fields

    Once you've added a few fields to your Form, you might want to change the order of questions.

    To change the order of fields:

    1. Click Reorder in the top-right corner of the Form designer.

    2. Drag and drop the fields into the desired order.

    3. Click I am done reordering to get back to the Form designer.

    Form Fields Reordered

    hashtag
    Form Pages

    Forms can be grouped into pages. When rendered, each page will be presented one at a time to the user. They will need to complete the first page before moving onto the second and can navigate back and forth between pages.

    To add a new page at the start or end of the form, use the buttons in the top right corner of the editing view.

    Add new page button at the top of Form

    You can also add a new page directly to the bottom of the form via the Add new page button. This will appear below other pages when at least one exists.

    hashtag
    Form Groups

    Within a page, form fields can be arranged into groups. These will display all together on a single page but can be styled so the fields are appropriately grouped in fieldsets.

    New groups are added via the Add new group button.

    Add new Group button

    hashtag
    Form Columns

    The last level of structure are columns that can be created within a group. To set the number of columns, click the cog icon next to the Group Name. You can now add or move fields to the new columns created.

    Form Columns

    hashtag
    Saving the Form

    Once you have created the Form, save the design by clicking the Save button.

    Form save Form

    hashtag
    Importing a Form

    Import Form Definition allows you to import a form into your Umbraco site using a predefined JSON file. This file contains the form’s structure, fields, validations, workflows, and settings.

    When you import a form definition, Umbraco uses the JSON structure to recreate the form as it was defined, enabling you to:

    • Reuse existing forms across multiple projects or environments.

    • Migrate forms between development, testing, and production environments.

    • Restore forms from backups or previously exported definitions.

    Using the Import Form Definition option, you can manage your forms without having to recreate them.

    Import a Form

    hashtag
    Organizing Forms in Folders

    If the product installation is set up to store form definitions in the database, you will be able to store forms within folders. This can help with organization and makes it easier to locate the forms for modification, especially if you plan to create many Forms.

    To create a folder:

    1. Go to the Forms section.

    2. Click + next to the Forms folder.

    3. Select Create > New Folder.

    4. Enter a Folder Name.

    5. Click Create Folder.

    You can create folders within folders, rename, move, import forms, delete, or reload children.

    Folder Options

    To move or copy forms into folders, click the ... next to the Form and select Move.

    Move Form in Folder

    hashtag
    Adding the Form to the Umbraco Site

    To add the Form, follow these steps:

    1. Go to the Content section of the Umbraco Backoffice.

    2. Open the content page where you want to display the form.

    The page you choose should have a Form Picker Data Type which you can add in the Settings section under Document Types.

    1. Click Choose and select the Form you want to insert.

    You will be able to select from the full list of forms. If available on your installation, you can select using a folder based view, which can be quicker to navigate when many forms have been prepared.

    Content page add macro
    1. Click Choose.

    2. Click Save and publish.

    In this section, you can learn more about the background for adding this check, as well as how to use and understand the results.

    hashtag
    Background

    A health check was introduced to confirm the Umbraco Forms database tables are all set up with the expected data integrity checks - i.e. primary keys, foreign keys and unique constraints.

    In most cases, you can expect them all to be in place without any developer intervention. For new installs, the database schema is initialized with all the necessary integrity constraints. And for upgrades, any new schema changes are automatically applied.

    There remains the possibility though that not all will be in place for a particular installation. For example, this could happen if a constraint is added in a new version. It can't be added via an automated migration due to existing data integrity issues.

    In particular, prior to version 8.7, there were a number of tables that weren't defined as strictly as they should be in this area. So we've added some primary key, foreign key and unique constraints with this version. If you've been running a version prior to this and are upgrading, these schema updates will be applied automatically unless there is existing data in the tables that prevent them from being added.

    There shouldn't be - but without these constraints in place it's always possible for an application bug to exist that allows for example the creation of duplicate records, or the orphaning of records, that aren't correct. This is the reason for the constraints to exist, and why we want to ensure they are in place.

    hashtag
    Running The Health Check

    To run the health check:

    1. Navigate to the Health Check dashboard in the Settings section in the Umbraco backoffice.

    2. Click on the Forms button and select Perform checks. You'll see a result that looks something like this:

    If you have a full set of green ticks, then you're all good - and no need to read on!

    If you have one or more red crosses though, that means a particular constraint wasn't able to be applied via the automatic schema migrations when you installed a new version of Umbraco Forms, due to existing data issues.

    It isn't essential that they are resolved - the package can and does function correctly without them - but for reasons of ensuring data integrity and performance, it is recommended that they are.

    hashtag
    Resolving Reported Problems

    When Umbraco Forms installs an upgrade, it will attempt to apply any schema changes. If though, the update isn't essential, and it can't proceed due to existing data integrity issues, the failed update will be logged and then the rest of the migration will continue.

    As well as in the log files, such issues will be visible via the health check and will need to be resolved by applying scripts directly to the database.

    To support this, we provide the following SQL scripts:

    • Apply database integrity schema changes for 8.7.0+ - 8.7.0-apply-keys-and-indexes

    • Apply database integrity schema changes for 8.7.0+ (Forms in database tables) - 8.7.0-apply-keys-and-indexes-forms-in-db

    The first of these provides the SQL statements required to apply the schema updates for 8.7.0+ to the common Umbraco Forms tables. The second applies to those tables used for when Forms are stored in the database, and hence only need to be applied if that option is configured.

    circle-info

    Before running any scripts or queries, be sure to have a database backup in place.

    To take an example, let's say that via the health check results you can see that the "Unique constraint on table 'UFForms', column 'Key' is missing."

    If you look in the SQL script you'll see that in order to apply this directly to the database, you would need to run the following SQL statement:

    If you run it though, you'll see the reason why the migration that ran when Umbraco Forms was upgraded couldn't apply the change:

    The constraint can't be applied if there are existing duplicate values, so first they need to be found and removed.

    To find duplicate values in the 'Key' field in this table you can run the following SQL statement:

    Running the statement above will list out the 'Key' fields that are duplicated in the table.

    To see the full details of the duplicate records, you can use this query:

    From the Id field you can identify the Form records that are duplicated and should be removed, and delete the records. To check you have found them all, run one of the above queries again, and confirm you find no records returned.

    Finally you can run the ALTER TABLE... statement shown above to apply the constraint, and confirm via the health check that it's now in place.

    By repeating similar steps as required, you'll be able to ensure that all recommended keys, constraints and indexes are in place.

    If for any reason you wish to revert the changes - perhaps when testing these updates in a non-production environment - reversion scripts for all the 8.7 updates are also provided:

    To support this, we provide the following SQL scripts:

    • Revert database integrity schema changes for 8.7.0+ - 8.7.0-apply-keys-and-indexes_revert

    • Revert database integrity schema changes for 8.7.0+ (Forms in database tables) - 8.7.0-apply-keys-and-indexes-forms-in-db_revert

    Health Checkarrow-up-right

    Setting Types

    Umbraco Forms field, prevalue source and workflow types are defined in C# and include one or more setting values.

    These settings are completed by the editor when using the type on their form.

    Each setting type can have it's own user interface. So a string can use a text box but a more complicated JSON structure can use a more appropriate user interface.

    From Forms 14, each interface is defined as an Umbraco property editor UIarrow-up-right.

    The user interface used for a particular setting is defined by the View property:

    If not specified, the default Umb.PropertyEditorUi.TextBox is used.

    hashtag
    Built-in setting types

    The following setting types are available and are used for the field, prevalue source and workflow types that ship with the package.

    Some are defined with the Umbraco CMS and some ship with the Forms package.

    Name
    Source
    Description
    Used in

    Most of the above setting types are used in one or more field, prevalue source and workflow types available with Umbraco Forms. For the less common ones, a usage has been indicated in the table.

    hashtag
    Additional setting types

    Some types we don't use within the package, but we make available for developers to use when creating their own types.

    For example Forms.PropertyEditorUi.TextWithFieldPicker. This offers the option of text field entry or the selection of a field from the form. This can be useful in workflows where you need to reference the value of a specific field.

    hashtag
    Creating a setting type

    It's also possible to define your own setting type using a combination of server and client-side code.

    Read how do this in the article on .

    Adding A Type To The Provider Model

    To add a new type, no matter if it's a workflow, field, data source, etc, there is a number of tasks to perform to connect to the Forms provider model. This chapter walks through each step and describes how each part works. This chapter will reference the creation of a workflow type. It is, however, the same process for all types.

    hashtag
    Preparations

    Create a new class library project in Visual Studio add references to the Umbraco.Forms.Core.dll (available via referencing the NuGet packagearrow-up-right). You might also need to reference Umbraco.Forms.Core.Providersarrow-up-right.

    hashtag
    Adding the type to Forms

    The Forms API contains a collection of classes that can be registered at startup or in an Umbraco component. So to add a new type to Forms you inherit from the right class. In the sample below we use the class for the workflow type.

    When you implement this class you get two methods added. One of them is Execute which performs the execution of the workflow and the other is a method which validates the workflow settings, we will get back to these settings later on.

    Any dependencies required that are registered with the dependency injection container can be provided via the constructor.

    Even though we have the class inheritance in place, we still need to add a bit of default information.

    hashtag
    Setting up basic type information

    Even though we have the class inheritance in place, we still need to add a bit of default information. This information is added in the class's constructor like this:

    All three are mandatory and the ID must be unique, otherwise the type might conflict with an existing one.

    hashtag
    Adding settings to a type

    Now that we have a basic class setup, we would like to pass setting items to the type. So we can reuse the type on multiple items but with different settings. To add a setting to a type, we add a property to the class, and give it a specific attribute like this:

    The Umbraco.Forms.Core.Attributes.Setting registers the property in Umbraco Forms and there will automatically be UI and storage generated for it. In the attribute, a name, description and the view to be rendered is defined.

    With the attribute in place, the property value is set every time the class is instantiated by Umbraco Forms. This means you can use the property in your code like this:

    For all types that use the provider model, settings work this way. By adding the Setting attribute Forms automatically registers the property in the UI and sets the value when the class is instantiated.

    Each setting value is stored as a string with the user interface for generating the value defined via the View property.

    Umbraco Forms ships with .

    hashtag
    Validate type settings with ValidateSettings()

    The ValidateSettings() method which can be found on all types supporting dynamic settings, is used for making sure the data entered by the user is valid and works with the type.

    hashtag
    Registering the class with Umbraco and Forms

    To register the type, ensure your web application project has a reference to the class library, either via a project or NuGet reference. Then add the following code into the startup pipeline. In this example, the registration is implemented as an extension method to IUmbracoBuilder and should be called from Program.cs:

    An alternative approach is to use a composer, as per this example:

    There are further convenience methods you can use for registering custom types. These are found in the namespace Umbraco.Forms.Core.Providers.Extensions.

    For example, instead of the following:

    Your workflow can be registered using:

    Or:

    Existing items that are not required in a particular installation can be removed with:

    Also look in the reference chapter for complete class implementations of workflows, fields and export types.

    hashtag
    Overriding default providers in Umbraco Forms

    It is possible to override and inherit the original provider, be it a Field Type or Workflow etc. The only requirement when inheriting a fieldtype that you wish to override is to ensure you do not override/change the Id set for the provider, and make sure your class is public.

    Here is an example of overriding the Textarea field aka Long Answer.

    As discussed in the previous section, you must also register the extended field type within a composer. You also need to create the the backoffice field type view.

    Composer:

    Backoffice View:

    Add a new HTML file as per the name of the field class (e.g. textareawithcount.html) to \wwwroot\App_Plugins\umbracoforms\Backoffice\Common\FieldTypes\ within your project. For this example, we can copy the original textarea.html file used by the standard 'Long Answer' field.

    The AngularJS client-side files are shipped with Umbraco Forms as part of a Razor Class Library. So you won't find these files on disk when you install the package.

    However if you do want to reference them you can view and extract them from the .

    Adding A Server-Side Notification Handler To Umbraco Forms

    See an example of validating a form server-side

    hashtag
    Form validation notification

    Add a new class to your project as a handler for the FormValidateNotification notification:

    The handler will check the ModelState and Form field values provided in the notification. If validation fails, we add a ModelError.

    circle-info

    Submitted field values are accessed via Field.Values. This property is populated before the notification fires for both standard form POST and headless API submissions.

    Previous versions of this documentation showed reading values from HttpContext.Request.Form. That approach only works for standard form posts and returns empty values for headless API submissions.

    When adding model errors, the key used must match the submission path:

    To register the handler, add the following code into the startup pipeline. In this example, the registration is implemented as an extension method to IUmbracoBuilder and should be called from Program.cs:

    hashtag
    Service notifications

    The services available via interfaces IFormService, IFolderService, IDataSourceService and IPrevalueSourceService trigger following notifications before or after an entity handled by the service is modified.

    The "-ing" events allow for the entity being changed to be modified before the operation takes place, or to cancel the operation. The "-ed" events fire after the update is complete.

    Both can be wired up using a composer and component:

    When a form or folder is moved there is no specific service event. However, information available in the State dictionary on the notification object can be used to determine whether the item was moved. If so, it can show where it was moved from:

    If a folder is being moved, the key within the State dictionary is "MovedFromParentId".

    hashtag
    Backoffice entry rendering events

    When an entry for a form is rendered in the backoffice, an event is available to allow modification of the record detail. This event is available before the record details are presented to the user. This is shown in the following example:

    Adding A Prevalue Source Type To Umbraco Forms

    This builds on the "" article

    Add a new class to your project - inherit it from Umbraco.Forms.Core.FieldPreValueSourceType and implement the class.

    The following example shows an illustrative custom prevalue source type that returns a hard-coded list of values. It can be extended for your needs via injection of services via the constructor. (See additional example at the bottom.)

    Dynamic settings can be applied and validated as shown in the article.

    You will then need to register this new prevalue source type as a dependency.

    Magic Strings

    Umbraco Forms has some magic strings that enable you to render values from various sources, such as session, cookies and Umbraco page fields.

    hashtag
    Where can I use magic strings?

    Magic strings can be used in form fields as a label, description or default value. As an example they can be used in default values in hidden fields - normally in the form of referral codes from a session, cookie or request item.

    These values can also be used for properties and settings in workflows. This means you can use name and email fields from a form to create a personal 'Thank you' email.

    Adding a UI Builder repository as a prevalue source

    When using alongside Umbraco Forms, you can use a configured UI Builder repository as a prevalue source.

    To do this, you will need to create a custom FieldPreValueSourceType source that uses UI Builder's SectionConfiguration property editor. Once configured, you can select a repository and fetch the prevalues from there.

    hashtag
    Example

    -- Adds unique constraint to UFForms.
    ALTER TABLE dbo.UFForms
    ADD CONSTRAINT UK_UFForms_Key UNIQUE NONCLUSTERED
    (
     [Key] ASC
    ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    GO
    The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.UFForms' and the index name 'UK_UFForms_Key'. The duplicate key value is (...).
    SELECT [Key]
    FROM UFForms
    GROUP BY [Key]
    HAVING COUNT(*) > 1
    SELECT *
    FROM UFForms
    WHERE [Key] IN (SELECT [Key]
     FROM UFForms
     GROUP BY [Key]
     HAVING COUNT(*) > 1
    )
    [Umbraco.Forms.Core.Attributes.Setting("Message", View = "Umb.PropertyEditorUi.TextBox")]
    public string Message { get; set; }
    using System.Linq;
    using Umbraco.Cms.Core.Events;
    using Umbraco.Forms.Core.Models;
    using Umbraco.Forms.Core.Services.Notifications;
    
    namespace MyFormsExtensions
    {
        /// <summary>
        /// Catch form submissions before being saved and perform custom validation.
        /// </summary>
        public class FormValidateNotificationHandler : INotificationHandler<FormValidateNotification>
        {
            public void Handle(FormValidateNotification notification)
            {
                // If needed, be selective about which form submissions you affect.
                if (notification.Form.Name == "Form Name")
                {
                    // Check the ModelState
                    if (notification.ModelState.IsValid == false)
                    {
                        return;
                    }
    
                    // A sample validation
                    var email = GetFieldValue(notification.Form, "email");
                    var emailConfirm = GetFieldValue(notification.Form, "verifyEmail");
    
                    // If the validation fails, return a ModelError.
                    if (email.ToLower() != emailConfirm.ToLower())
                    {
                        // Standard form POST renders validation messages keyed by field Id,
                        // while the headless API returns errors keyed by field alias.
                        Field verifyEmailField = GetField(notification.Form, "verifyEmail")!;
                        var errorKey = notification.Context.Request.HasFormContentType
                            ? verifyEmailField.Id.ToString()
                            : verifyEmailField.Alias;
    
                        notification.ModelState.AddModelError(errorKey, "Email does not match");
                    }
                }
            }
    
            /// <summary>
            /// Gets the submitted value for a field by its alias.
            /// </summary>
            /// <remarks>
            /// Field.Values is populated before the notification fires in both submission paths
            /// (standard form POST and headless API JSON submissions), making it the reliable way
            /// to access submitted values regardless of how the form was submitted.
            /// </remarks>
            private static string GetFieldValue(Form form, string alias)
            {
                Field? field = GetField(form, alias);
                return field?.Values.FirstOrDefault()?.ToString()?.Trim() ?? string.Empty;
            }
    
            private static Field? GetField(Form form, string alias)
                => form.AllFields.SingleOrDefault(f => f.Alias == alias);
        }
    }

    The "Save as Umbraco node" workflow

    Umb.PropertyEditorUi.DocumentPicker

    CMS

    Uses a content picker

    Umb.PropertyEditorUi.Dropdown

    CMS

    Used for selection from a list of options

    Umb.PropertyEditorUi.Integer

    CMS

    Uses numerical text box for entry

    Umb.PropertyEditorUi.MediaEntityPicker

    CMS

    Uses a media item picker for entry

    The "Send email with XSLT template" workflow

    Umb.PropertyEditorUi.MultipleTextString

    CMS

    Uses multiple text boxes for entry

    Umb.PropertyEditorUi.RadioButtonList

    CMS

    Uses multiple radio buttons for entry

    Umb.PropertyEditorUi.Slider

    CMS

    Uses a slider for range input

    The "reCAPTCHAv3" field type

    Umb.PropertyEditorUi.TextArea

    CMS

    Uses a multiline textbox for entry

    Umb.PropertyEditorUi.TextBox

    CMS

    Uses a single-line textbox for entry

    Umb.PropertyEditorUi.Tiptap

    CMS

    Uses a rich text editor for input

    The "Send email" workflows

    Umb.PropertyEditorUi.Toggle

    CMS

    Uses a single checkbox for entry

    Umb.PropertyEditorUi.UploadField

    CMS

    Used for selection of a file

    The "Text file" prevalue source

    Forms.PropertyEditorUi.DataTypePicker

    Forms

    Uses a datatype picker

    The "Umbraco prevalues" prevalue source

    Forms.PropertyEditorUi.DocumentTypePicker

    Forms

    Uses a Document Type picker

    The "Umbraco nodes" prevalue source

    Forms.PropertyEditorUi.DocumentTypeFieldPicker

    Forms

    Uses to select fields from a Document Type

    The "Umbraco nodes" prevalue source

    Forms.PropertyEditorUi.DocumentMapper

    Forms

    Used for mapping of fields from a Document Type

    The "Save as Umbraco node" workflow

    Forms.PropertyEditorUi.EmailTemplatePicker

    Forms

    Used for selection of an email template

    The "Send email with Razor template" workflow

    Forms.PropertyEditorUi.FieldMapper

    Forms

    Used to map fields from a form to required aliases

    The "Send to URL" workflow

    Forms.PropertyEditorUi.Password

    Forms

    Uses password text box for entry

    Forms.PropertyEditorUi.StandardFieldMapper

    Forms

    Used to map system fields from a form to required aliases

    The "Send to URL" workflow

    Forms.PropertyEditorUi.TextWithFieldPicker

    Forms

    Uses a single-line textbox/form field list for entry

    Umb.PropertyEditorUi.CheckBoxList

    CMS

    Uses multiple checkboxes for entry

    Umb.PropertyEditorUi.ContentPicker.Source

    CMS

    adding a field type
    Text with field picker

    Uses a content picker with the option for XPath entry

    Attaching Workflows to Umbraco Forms
    setting types and you can also create your own
    Umbraco.Forms.StaticAssets NuGet packagearrow-up-right

    Standard form POST: use field.Id.ToString() (the form view renders validation messages keyed by field Id).

  • Headless API: use field.Alias (the API returns validation errors keyed by field alias).

  • You can check notification.Context.Request.HasFormContentType to determine which path is in use.

    circle-info

    The PreValue model in Umbraco Forms Versions 8.13.0, 9.5.0, 10.1.0, and above includes a .Caption property. This property is set separately from the .Value property. In the previous versions, the Value is generally used as the caption when rendered on the form.

    hashtag
    Another Example Using Dependency Injection to Access Additional Services

    This example will take a user-provided Content Node and create a custom Prevalue list from the property data on that node. Your own FieldPreValueSourceType can get its data from wherever you like - an API call, custom functions, etc.

    You will then need to register this new type as a dependency (either in Program.cs or in your own IComposer, as shown here).

    Adding a type to the provider model
    Validate type settings with ValidateSettings()
    public class LogWorkflow : Umbraco.Forms.Core.WorkflowType
    {
        private readonly ILogger<LogWorkflow> _logger;
    
        public LogWorkflow(ILogger<LogWorkflow> logger)
        {
            _logger = logger;
        }
    
        public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context)
        {
            throw new NotImplementedException();
        }
    
        public override List<Exception> ValidateSettings() {
            throw new NotImplementedException();
        }
    }
    public LogWorkflow(ILogger<LogWorkflow> logger) {
    
        _logger = logger;
    
        this.Name = "The logging workflow";
        this.Id = new Guid("D6A2C406-CF89-11DE-B075-55B055D89593");
        this.Description = "This will save an entry to the log";
    }
    [Umbraco.Forms.Core.Attributes.Setting("Log Header",
            Description = "Log item header",
            View = "TextField")]
    public string LogHeader { get; set; }
    [Umbraco.Forms.Core.Attributes.Setting("Document ID",
            Description = "Node the log entry belongs to",
            View = "Pickers.Content")]
    public string Document { get; set; }
    
    public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context) {
        _logger.LogInformation("Record submitted from: {IP}", context.Record.IP);
        return WorkflowExecutionStatus.Completed;
    }
    public override List<Exception> ValidateSettings() {
        List<Exception> exceptions = new List<Exception>();
        int docId = 0;
        if (!int.TryParse(Document, out docId))
            exceptions.Add(new Exception("Document is not a valid integer"));
        return exceptions;
    }
    public static IUmbracoBuilder AddUmbracoFormsCustomProviders(this IUmbracoBuilder builder)
    {
        builder.WithCollectionBuilder<WorkflowCollectionBuilder>()
            .Add<LogWorkflow>();
    }
    public class UmbracoFormsCustomProvidersComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
            builder.WithCollectionBuilder<WorkflowCollectionBuilder>()
                .Add<LogWorkflow>();
        }
    }
        builder.WithCollectionBuilder<WorkflowCollectionBuilder>()
            .Add<LogWorkflow>();
        builder.AddFormsWorkflow<LogWorkflow>():
        builder.FormsWorkflows().Add<LogWorkflow>();
        builder.FormsWorkflows().Exclude<Slack>();
    public class TextareaWithCount : Umbraco.Forms.Core.Providers.FieldTypes.Textarea
    {
        // Added a new setting when we add our field to the form
        [Umbraco.Forms.Core.Attributes.Setting("Max length",
        Description = "Max length",
        View = "TextField")]
        public string MaxNumberOfChars { get; set; }
    
        public TextareaWithCount()
        {
            // Set a different view for this fieldtype
            this.FieldTypeViewName = "FieldType.TextareaWithCount.cshtml";
    
            // We can change the default name of 'Long answer' to something that suits us
            this.Name = "Long Answer with Limit";
        }
    
        public override IEnumerable<string> ValidateField(Form form, Field field, IEnumerable<object> postedValues, HttpContext context, IPlaceholderParsingService placeholderParsingService, List<string> errors)
        {
            var baseValidation = base.ValidateField(form, field, postedValues, context, placeholderParsingService, errors);
            var value = postedValues.FirstOrDefault();
    
            if (value != null && value.ToString().Length < int.Parse(MaxNumberOfChars))
            {
                return baseValidation;
            }
    
            var custom = new List<string>();
            custom.AddRange(baseValidation);
            custom.Add("String is way way way too long!");
    
            return custom;
        }
    }
    public class UmbracoFormsCustomProvidersComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
            builder.FormsFields().Add<TextareaWithCount>();
        }
    }
    public static IUmbracoBuilder AddUmbracoFormsCoreProviders(this IUmbracoBuilder builder)
    {
        builder.AddNotificationHandler<FormValidateNotification, FormValidateNotificationHandler>();
    }
        public class TestSiteComposer : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.AddNotificationHandler<FormSavingNotification, FormSavingNotificationHandler>();
            }
        }
    
        public class FormSavingNotificationHandler : INotificationHandler<FormSavingNotification>
        {
            public void Handle(FormSavingNotification notification)
            {
                foreach (Form form in notification.SavedEntities)
                {
                    foreach (Page page in form.Pages)
                    {
                        foreach (FieldSet fieldset in page.FieldSets)
                        {
                            foreach (FieldsetContainer fieldsetContainer in fieldset.Containers)
                            {
                                foreach (Field field in fieldsetContainer.Fields)
                                {
                                    field.Caption += " (updated)";
                                }
                            }
                        }
                    }
                }
            }
        }
        public class TestSiteComposer : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.AddNotificationHandler<FormSavingNotification, FormSavingNotificationHandler>();
            }
        }
    
        public class FormSavingNotificationHandler : INotificationHandler<FormSavingNotification>
        {
            private readonly ILogger<FormSavingNotification> _logger;
    
            public FormSavingNotificationHandler(ILogger<FormSavingNotification> logger) => _logger = logger;
    
            public void Handle(FormSavingNotification notification)
            {
                foreach (Form savedEntity in notification.SavedEntities)
                {
                    _logger.LogInformation($"Form updated. New parent: {savedEntity.FolderId}. Old parent: {notification.State["MovedFromFolderId"]}");
                }
            }
        }
        public class TestSiteComposer : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.AddNotificationHandler<EntrySearchResultFetchingNotification, EntrySearchResultFetchingNotificationHandler>();
            }
        }
    
        public class EntrySearchResultFetchingNotificationHandler : INotificationHandler<EntrySearchResultFetchingNotification>
        {
            public void Handle(EntrySearchResultFetchingNotification notification)
            {
                var transformedFields = new List<object>();
                foreach (var field in notification.EntrySearchResult.Fields)
                {
                    if (field?.ToString() == "Test")
                    {
                        transformedFields.Add("Test (updated)");
                    }
                    else
                    {
                        transformedFields.Add(field);
                    }
                }
    
                notification.EntrySearchResult.Fields = transformedFields;
            }
        }
    using System;
    using System.Collections.Generic;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Models;
    
    namespace MyFormsExtensions
    {
        public class FixedListPrevalueSource : FieldPreValueSourceType
        {
            public FixedListPrevalueSource()
            {
                Id = new Guid("42C8158D-2AA8-4621-B653-6A63C7545768");
                Name = "Fixed List";
                Description = "Example prevalue source providing a fixed list of values.";
            }
    
            public override List<PreValue> GetPreValues(Field field, Form form) =>
                new List<PreValue>
                {
                    new PreValue
                    {
                        Id = 1,
                        Value = "item-one",
                        Caption = "Item One"
                    },
                    new PreValue
                    {
                        Id = 2,
                        Value = "item-two",
                        Caption = "Item Two"
                    }
                };
    
            /// <inheritdoc/>
            public override List<Exception> ValidateSettings()
            {
            // this is used to validate any dynamic settings you might apply to the PreValueSource
            // if there are no dynamic settings, return an empty list of Exceptions:
                var exceptions = new List<Exception>();
                return exceptions;
            }
        }
    }
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Cms.Core.DependencyInjection;
    using Umbraco.Forms.Core.Providers;
    
    namespace MyFormsExtensions
    {
        public class Startup : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.WithCollectionBuilder<FieldPreValueSourceCollectionBuilder>()
                    .Add<FixedListPrevalueSource>();
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using Microsoft.Extensions.Logging;
    using Umbraco.Cms.Core;
    using Umbraco.Cms.Core.Models.PublishedContent;
    using Umbraco.Cms.Core.Web;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Models;
    namespace MyFormsExtensions
        public class FormPrevaluesSourceNode : FieldPreValueSourceType
        {
            private readonly ILogger _logger;
            private readonly IUmbracoContextFactory _UmbracoContextFactory;
            //DEFINE ANY CONFIGURATION SETTING HERE
            [Umbraco.Forms.Core.Attributes.Setting(name: "Source Node",
                Alias = "SourceNodeId",
                Description = "Node holding the Options desired.",
                View = "pickers.content")]
            public string SourceNodeId { get; set; }
            public FormPrevaluesSourceNode(
                ILogger<FormPrevaluesSourceNode> logger
                , IUmbracoContextFactory umbracoContextFactory
            )
            {
                _logger = logger;
                _UmbracoContextFactory = umbracoContextFactory;
                this.Id = new Guid("0E4D4E2B-56E1-4E86-84E4-9A0A6051B57C"); //MAKE THIS UNIQUE!
                this.Name = "Content-defined Form Prevalues Source Node";
                this.Description = "Select a node of type 'FormPrevaluesSourceNode'";
                this.Group = "Custom";
                this.Icon = "icon-science";
            }
            /// <summary>
            /// The main method where the PreValues are defined and returned.
            /// </summary>
            /// <param name="field"></param>
            /// <param name="form"></param>
            /// <returns>List of 'Umbraco.Forms.Core.Models.PreValue'</returns>
            public override List<PreValue> GetPreValues(Field field, Form form)
            {
                List<PreValue> result = new List<PreValue>();
                try
                {
                    // Access the Configuration Setting and check that is is valid
                    if (!string.IsNullOrEmpty(SourceNodeId))
                    {
                        var nodeId = 0;
                        var isValidId = Int32.TryParse(SourceNodeId, out nodeId);
                        if (isValidId)
                        {
                            IPublishedContent iPub;
                            using (var umbracoContextReference = _UmbracoContextFactory.EnsureUmbracoContext())
                            {
                                iPub = umbracoContextReference.UmbracoContext.Content.GetById(nodeId);
                            }
                            if (iPub != null)
                            {
                                int sort = 0;
                                //This is using a ModelsBuilder Model to strongly-type the selected node
                                var preValSourceNode = new Models.FormPrevaluesSourceNode(iPub, null);
                                foreach (var prevalue in preValSourceNode.PreValues)
                                {
                                    PreValue pv = new PreValue();
                                    pv.Id = $"{iPub.Id}-{sort}";
                                    pv.Value = prevalue.StoredValue;
                                    pv.Caption = prevalue.DisplayText; //.Caption only available in Forms Versions  8.13.0+, 9.5.0+, & 10.1.0+
                                    pv.SortOrder = sort;
                                    result.Add(pv);
                                    sort++;
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError($"Unable to get options from FormPrevaluesSourceNode #{SourceNodeId}", ex);
                }
                return result;
            }
            /// <summary>
            /// This is where any checks for Configuration validity are done.
            /// The exceptions will be displayed in the back-office UI to the user.
            /// </summary>
            /// <returns>List of 'System.Exception'</returns>
            public override List<Exception> ValidateSettings()
            {
                List<Exception> exceptions = new List<Exception>();
                if (string.IsNullOrEmpty(SourceNodeId))
                {
                    exceptions.Add(new Exception("'Source Node' setting not filled out"));
                }
                else
                {
                    var nodeId = 0;
                    var isValidId = Int32.TryParse(SourceNodeId, out nodeId);
                    if (isValidId)
                    {
                        IPublishedContent iPub;
                        using (var umbracoContextReference = _UmbracoContextFactory.EnsureUmbracoContext())
                        {
                            iPub = umbracoContextReference.UmbracoContext.Content.GetById(nodeId);
                        }
                        if (iPub != null && iPub.ContentType.Alias != Models.FormPrevaluesSourceNode.ModelTypeAlias)
                        {
                            exceptions.Add(new Exception("'Source Node' needs to be of type 'FormPrevaluesSourceNode'"));
                        }
                    }
                }
                return exceptions;
            }
        }
    }
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Cms.Core.DependencyInjection;
    using Umbraco.Forms.Core.Providers;
    namespace MyFormsExtensions
    {
        public class FormsComposer : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                //Adding Custom Form PreValueSource
                builder.WithCollectionBuilder<FieldPreValueSourceCollectionBuilder>()
                    .Add<FormPrevaluesSourceNode>();
            }
        }
    }
    hashtag
    Sources of magic string values

    hashtag
    Request

    [@SomeRequestItem] this allows you to display an item from the current HttpContext.Request with the key of 'SomeRequestItem'.

    Some examples of variables that are normally available in HttpContext.Request:

    • [@Url]: Insert the current URL

    • [@Http_Referer]: The previous visited URL (if available)

    • [@Remote_Addr]: The IP address of the visitor (stored by default by Umbraco)

    • [@Http_User_Agent]: The browser of the visitor

    The variables are not case-sensitive.

    You can use it for any available query string variable in the URL as well. If your URL has the query string [email protected], you can get the value of the query string into your field by using [@email].

    hashtag
    Dictionary Items

    For multi-lingual websites, rather than hard-coding labels like form field captions, a dictionary key can be entered as, for example, #MyKey. When the form is rendered, the placeholder will be replaced by the value of the dictionary item identified by the key, according to the current language.

    In most cases, the field must contain only the magic string for the replacement to be carried out. This makes sense for translated values, as you will want the whole phrase replaced when, for example, using one for a field's placeholder.

    We also translate dictionary keys found within the rich text field, which will be contained within HTML tags. Here we look for dictionary keys making up the full inner text of a tag. So for example, <p>#myKey</p> would be translated, but <p>Lorem ipsum #myKey dolor sit amet.</p> would not.

    hashtag
    Session & Cookies

    [%SomeSessionOrCookieItem] this allows you to display an item from the current HttpContext.Session with the key of 'SomeSessionOrCookieItem'. The session key can only contain alphanumeric chars and you cannot use dots for example. [%Member.Firstname] cannot be used, but [%MemberFirstname] can be used. You would have to fill these session keys yourself.

    If the item cannot be found in the collection of session keys, it will then try to find the item from the HttpContext.Cookies collection with the same key.

    hashtag
    Umbraco Page field

    [#myUmbracoField] this allows you to insert a property of that page and is based on the alias of the field. If your page has a property with the alias 'title', you can use [#title] in your form.

    Some extra variables are:

    • [#pageName]: The nodename of the current page

    • [#pageID]: The node ID of the current page

    hashtag
    Recursive Umbraco Page field

    [$myRecursiveItem] this allows you to parse the Umbraco Document Type property myRecursiveItem. So if the current page does not contain a value for this then it will request it from the parent up until the root or until it finds a value.

    hashtag
    Additional data

    When rendering a form, additional data can be provided in the form of a dictionary. As well as being associated with the created record and available within workflows, they can be used for "magic string" replacements.

    They are accessed using this syntax: [+additionalDataKey].

    hashtag
    Umbraco Form field

    {myAliasForFormField} this allows you to display the entered value for that specific field from the form submission. Used in workflows to send an automated email back to the customer based on the email address submitted in the form. The value here needs to be the alias of the field, and not the name of the field.

    Some extra variables are:

    • {record.id}: The ID of the current record - this is only accessible on workflows triggered "on approve" or "on reject" rather than "on submit"

    • {record.updated}: The updated date/time of the current record

    • {record.created}: The created date/time of the current record

    • {record.umbracopageid}: The Umbraco Page ID the form was submitted on

    • {record.uniqueid}: The unique ID of the current record

    • {record.ip}: The IP address that was used when the form was submitted

    • {record.memberkey}: The member key that was used when the form was submitted

    hashtag
    Member properties from a form submission

    {member.FOO} with the prefix of member, the same syntax will allow you to retrieve information about the submission if it was submitted by a logged-in member.

    hashtag
    Formatting magic strings

    Using a magic string such as in the examples above will output the values exactly as read from the source. It's possible to apply a format string to customize the output.

    The syntax follows that of AngularJS filters, i.e. [<magic-string> | <formatFunction>: <arg1>: <arg2>].

    For example, to truncate a string value read from an Umbraco page field with alias title, you would use:

    Umbraco Forms ships with the following filters:

    Filter
    Function
    Arguments
    Example

    Bound a number

    bound

    min and max bound

    [#field | bound: 1: 10]

    Convert string to lower case

    lower

    The format strings used for formatting dates and numbers are the standard or custom .NET datearrow-up-right and numericarrow-up-right format strings respectively.

    Further magic string format functions can be created in code for use in forms.

    hashtag
    How can I parse these values elsewhere in my C# code or Razor Views?

    A service implemented by the IPlaceholderParsingService interface is available for use in custom code or views. It's found in the Umbraco.Forms.Core.Services namespace.

    In a controller you can inject it via the constructor and it can also be injected into views via:

    The interface implements a single method, ParsePlaceHolders, that can be used for parsing magic strings. There are a few overloads available for use depending on the context.

    If parameters for the Record or Form are omitted, magic strings relating to these objects will be removed.

    There is also a public extension method ParsePlaceHolders() extending the string object in the Umbraco.Forms.Core.Extensions namespace, again available with some overloads allowing the provision of a Form or Record object if available.

    The following class shows how to create a UIBuilderRepository prevalue source type.

    And registered with:

    This will then make the UI Builder Repository available when creating a new prevalue source:

    UI Builder Repository available as a prevalue source

    Once selected, you can pick a Section, Collection, and Data View from the configured UI Builder repositories:

    Selecting a UI Builder repository as a prevalue source
    Umbraco UI Builderarrow-up-right
    public class MyComposer : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
          builder.WithCollectionBuilder<FieldPreValueSourceCollectionBuilder>()
                .Add<UIBuilderRepository>();
        }
    }

    Release Notes

    Get an overview of the things changed and fixed in each version of Umbraco Forms.

    In this section, we have summarized the changes to Umbraco Forms released in each version. Each version is presented with a link to the Forms issue trackerarrow-up-right showing a list of issues resolved in the release. We also link to the individual issues themselves from the detail.

    If there are any breaking changes or other issues to be aware of when upgrading they are also noted here.

    circle-info

    If you are upgrading to a new major version, you can find information about the breaking changes in the Version Specific Upgrade Notes article.

    hashtag
    Release history

    This section contains the release notes for Umbraco Forms 17 including all changes for this version.

    hashtag
    (April 2nd 2026)

    hashtag
    Analytics

    Umbraco Forms now includes built-in analytics that provide insight into how your forms are performing. You can view submission trends over time, monitor workflow success rates, and identify which pages are driving submissions.

    hashtag
    Other

    • Umbraco CMS dependency updated to 17.3.0

    • Optimize record collection query performance

    • Add per-row entity actions to entries table

    hashtag
    17.2.1

    • Fix: string length validation for file upload fields

    hashtag
    (March 5th 2026)

    • File Upload: Treat JPEG and JPG as equivalent in validation

    • Field Preview: Fix CSS selector for horizontal prevalue spacing

    • Password Field: Fix unknown property editor UI for autocomplete setting

    hashtag
    17.2.0-rc2 (February 26th 2026)

    • Add global backoffice search for Forms

    • Fix regression with new Forms missing default page, group and data consent field

    • Fix reCAPTCHA Enterprise field not correctly validating assessments

    hashtag
    17.2.0-rc (February 19th 2026)

    • Umbraco CMS dependency updated to 17.2.0

    • Add ARIA attributes for form validation accessibility

    • Fix conditions not seeing field values modified by previous workflows

    hashtag
    17.1.3 (February 12th 2026)

    • Fix reCAPTCHA Enterprise script not loading correctly on the frontend

    hashtag
    (January 30th 2026)

    • Fix export path resolution for paths starting with ~

    hashtag
    (January 29th 2026)

    • Ensure entries selection can be cleared correctly after delete

    • Ensure entries selection can be cleared completely

    • Add additional exports for NPM package

    hashtag
    (January 22nd 2026)

    • All items detailed under release candidates for 17.1.0.

    hashtag
    (January 15th 2026)

    • Add confirmation modal for entry bulk deletion

    • Fix prevalue source validation

    • Fix prevalue source dropdown rendering

    hashtag
    (January 8th 2026)

    hashtag
    reCAPTCHA Enterprise field type added

    A new reCAPTCHA Enterprise field type has been added, providing advanced bot protection using Google's reCAPTCHA Enterprise service.

    circle-exclamation

    The "Score Threshold" setting for the reCAPTCHA Enterprise field type is currently not fully functional. This is due to a dependent issue in Umbraco CMS (see ). This will be resolved in a future release.

    hashtag
    Unconfigured reCAPTCHA fields now display as disabled

    The reCAPTCHA v2, v3, and Enterprise field types now display as disabled in the form designer when their respective settings are not configured. This prevents editors from adding unconfigured reCAPTCHA fields that would not work on the frontend.

    This change also ensures that field types remain registered. This prevents issues when transferring forms with Umbraco Deploy between environments where reCAPTCHA settings may not yet be configured.

    • Umbraco CMS dependency updated to 17.1.0

    • Add reCAPTCHA Enterprise field type

    • Preserving line-spacing in text-area input

    hashtag
    (December 17th 2025)

    • Fix bug with NPM package exports not resolving correctly

    hashtag
    (December 11th 2025)

    • Refactored UX for sorting on form designer

    • Render uploaded files as semantically correct HTML

    • Filter out fields from email workflows when 'Include Sensitive Data' is set to false

    hashtag
    17.0.1 (November 27th 2025)

    • Fix issues with the 17.0.0 release where migrations would sometimes not complete successfully

    hashtag
    (November 27th 2025)

    • Update Forms dependencies to 17.0.0

    • All items detailed under release candidates for 17.0.0.

    • JavaScript now correctly finds the form config element when it is not adjacent

    hashtag
    17.0.0-rc4 (November 25th 2025)

    • Stop "Save and preview" modal from displaying an interstitial state

    • Adds additional exports to @umbraco-forms/backoffice NPM package

    • Razor email templates now format URLs as UrlMode.Absolute

    hashtag
    17.0.0-rc3 (November 20th 2025)

    • Update dependencies to 17.0.0-rc3

    • Fix issue where "Save and preview" modal would flash with no content

    • Fix bug that showed "Empty due to Umbraco Forms in trial mode" for entries even with a valid license

    hashtag
    17.0.0-rc2 (November 13th 2025)

    • Update dependencies to 17.0.0-rc2

    hashtag
    17.0.0-rc1 (October 30th 2025)

    • Update dependencies to 17.0.0-rc1

    hashtag
    Legacy release notes

    You can find the release notes for versions out of support in the and .

    Email Templates

    Creating an email template for Umbraco Forms.

    We include a Workflow Send email with template (Razor) that allows you to pick a Razor view file that can be used to send out a pretty HTML email for Form submissions.

    hashtag
    Creating an Email Template

    If you wish to have one or more templates to choose from the Send email with template (Razor), you will need to place all email templates into the ~/Views/Partials/Forms/Emails/ folder.

    The Razor view must inherit from FormsHtmlModel:

    You now have a model that contains your Form fields which can be used in your email HTML markup, along with the UmbracoHelper methods such as Umbraco.TypedContent and Umbraco.TypedMedia etc.

    Below is an example of an email template from the ~/Views/Partials/Forms/Emails/ folder:

    Adding An Export Type To Umbraco Forms

    This builds on the "" chapter.

    Add a new class to your project and have it inherit from Umbraco.Forms.Core.ExportType. You have two options when implementing the class, as shown in the following examples.

    hashtag
    Basic Example

    You can implement the method public override Task<string> ExportRecordsAsync(Guid formId, RecordExportFilter filter)

    Customize Default Fields and Workflows For a Form

    How to amend the built-in behavior of adding fields and associating workflows with new forms

    By default, a single workflow is added when a new form is created. This workflow will send a copy of the form to the email address of the current backoffice user.

    A single "data consent" field will also be added unless it has been disabled via configuration.

    It's possible to amend this behavior and change it to fit your needs.

    hashtag
    Implementing a Custom Behavior

    [#title | truncate: 10]
    @using Umbraco.Forms.Core.Services;
    @inject IPlaceholderParsingService PlaceholderParsingService
    using System.Text.Json;
    using System.Text.Json.Serialization;
    using Umbraco.Cms.Core.Models;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Attributes;
    using Umbraco.Forms.Core.Models;
    using Umbraco.UIBuilder.Configuration;
    using Umbraco.UIBuilder.Services;
    
    namespace MyFormsExtensions
    {
        public class UIBuilderRepository : FieldPreValueSourceType
        {
            private readonly UIBuilderConfig _config;
            private readonly EntityService _entityService;
    
            public UIBuilderRepository(
                UIBuilderConfig config,
                EntityService entityService)
            {
                Id = new Guid("ED56A31B-56FD-41EA-9D21-755750879D13");
                Name = "UI Builder Repository";
                Alias = "uiBuilderRepository";
                Description = "Use a UI Builder repository as a prevalue source.";
                Icon = "icon-file-cabinet";
    
                _config = config;
                _entityService = entityService;
            }
    
            [Setting("Source", Description = "Select the source section, collection and data view", View = "UiBuilder.PropertyEditorUi.EntityPicker.SectionConfiguration", IsMandatory = true, DisplayOrder = 10)]
            public virtual string Source { get; set; } = string.Empty;
    
            public override Task<List<PreValue>> GetPreValuesAsync(Field? field, Form? form)
            {
                var result = new List<PreValue>();
    
                UIBuilderSectionConfiguration? configuration = ParseSourceConfiguration();
                if (configuration != null)
                {
                    if (!string.IsNullOrEmpty(configuration.Collection))
                    {
                        CollectionConfig collectionConfig = _config.Collections[configuration.Collection];
                        IEnumerable<object> entities;
    
                        if (string.IsNullOrEmpty(configuration.DataView))
                        {
                            entities = _entityService.GetAllEntities(collectionConfig);
                        }
                        else
                        {
                            var allEntities = new List<object>();
                            const int pageSize = 100;
                            int pageNumber = 1;
                            long totalPages = 0;
    
                            while (pageNumber == 1 || pageNumber <= totalPages)
                            {
                                PagedResult<object> pagedResult = _entityService.FindEntities(
                                    collectionConfig,
                                    pageNumber: pageNumber,
                                    pageSize: pageSize,
                                    dataViewAlias: configuration.DataView);
    
                                if (pagedResult.Items != null)
                                {
                                    allEntities.AddRange(pagedResult.Items);
                                }
    
                                if (pageNumber == 1)
                                {
                                    totalPages = pagedResult.TotalPages;
                                }
    
                                pageNumber++;
                            }
    
                            entities = allEntities;
                        }
    
                        foreach (var entity in entities)
                        {
                            var id = GetPropertyValue(collectionConfig.IdProperty, entity);
                            var name = GetPropertyValue(collectionConfig.NameProperty, entity);
    
                            result.Add(new PreValue()
                            {
                                Id = id?.ToString() ?? string.Empty,
                                Caption = name?.ToString() ?? string.Empty,
                                Value = id?.ToString() ?? string.Empty
                            });
                        }
                    }
                }
    
                result = result.OrderBy(x => x.Id).ToList();
                return Task.FromResult(result);
            }
    
            public override List<Exception> ValidateSettings()
            {
                var exceptions = new List<Exception>();
    
                UIBuilderSectionConfiguration? configuration = ParseSourceConfiguration();
                if (configuration != null)
                {
                    if (string.IsNullOrEmpty(configuration.Section))
                    {
                        exceptions.Add(new Exception("'Section' setting has not been set"));
                    }
    
                    if (string.IsNullOrEmpty(configuration.Collection))
                    {
                        exceptions.Add(new Exception("'Collection' setting has not been set"));
                    }
                }
    
                return exceptions;
            }
    
            private UIBuilderSectionConfiguration? ParseSourceConfiguration()
            {
                if (string.IsNullOrEmpty(Source))
                {
                    return null;
                }
    
                List<UIBuilderSectionConfigItem>? config = JsonSerializer.Deserialize<List<UIBuilderSectionConfigItem>>(Source);
                var result = new UIBuilderSectionConfiguration();
    
                if (config != null)
                {
                    foreach (UIBuilderSectionConfigItem item in config)
                    {
                        switch (item.Alias)
                        {
                            case "section":
                                result.Section = item.Value;
                                break;
                            case "collection":
                                result.Collection = item.Value;
                                break;
                            case "dataView":
                                result.DataView = item.Value;
                                break;
                            case "labels":
                                if (!string.IsNullOrEmpty(item.Value))
                                {
                                    result.Labels = JsonSerializer.Deserialize<UIBuilderLabels>(item.Value);
                                }
                                break;
                        }
                    }
                }
    
                return result;
            }
    
            private static object? GetPropertyValue(PropertyConfig? propertyConfig, object entity)
            {
                if (propertyConfig == null)
                {
                    return null;
                }
    
                return propertyConfig.PropertyGetter?.Invoke(entity) ?? propertyConfig.PropertyExpression.Compile().DynamicInvoke(entity);
            }
        }
    
        internal class UIBuilderSectionConfigItem
        {
            [JsonPropertyName("alias")]
            public string? Alias { get; set; }
    
            [JsonPropertyName("value")]
            public string? Value { get; set; }
        }
    
        internal class UIBuilderSectionConfiguration
        {
            public string? Section { get; set; }
            public string? Collection { get; set; }
            public string? DataView { get; set; }
            public UIBuilderLabels? Labels { get; set; }
        }
    
        internal class UIBuilderLabels
        {
            [JsonPropertyName("plural")]
            public string? Plural { get; set; }
    
            [JsonPropertyName("singular")]
            public string? Singular { get; set; }
        }
    }

    [#field | lower]

    Convert string to upper case

    upper

    [#field | upper]

    Format a number

    number

    format string

    [#field | number: #0.##%]

    Format a number as a currency

    currency

    [#field | currency]

    Format a date

    date

    format string

    [#field | date: dd-MM-yyyy HH:mm]

    HTML encode a string

    html

    [#field | html]

    Truncate a string

    truncate

    number of characters

    [#field | truncate: 10]

    Redesigned Field and Workflow modals to match CMS style and improve usability
  • Fix sort order and group for field type not being respected #1634arrow-up-right

  • Fix many column overflow in sidebar modal #1608arrow-up-right

  • Fix Rich Text editor not allowing block picks in "Thank you" workflow #1194arrow-up-right

  • Fix workflow failing to submit with decimal field #1574arrow-up-right

  • Fix error when viewing entries without Manage Forms permission #1638arrow-up-right

  • Use native textarea for validation regex #1642arrow-up-right

  • Fix data consent field included when created from template #1651arrow-up-right

  • Add validation for prevalue options on dropdown and similar fields #1660arrow-up-right

  • Fix text in tags/actions in forms overview wrapping on multiple lines #1661arrow-up-right

  • Fix deleting a selected prevalue throwing an error #1666arrow-up-right

  • Fix store records setting not respected from template #1669arrow-up-right

  • Fix save & preview opening modal with blank node names #1670arrow-up-right

  • Fix "Updated By" not updating accordingly #1671arrow-up-right

  • Fix creating form inside folder adding the form to root #1672arrow-up-right

  • Fix value failing to save #1673arrow-up-right

  • Fix page-specific magic strings not working in delivery API #1674arrow-up-right

  • Fix ManageSecurityWithUserGroups not working #1675arrow-up-right

  • Fix form entries not visible in Firefox #1677arrow-up-right #1678arrow-up-right

  • Resolve property editor UI from configured Data Type #1680arrow-up-right

  • Add filter by record state (Submitted/Approved/Rejected) to entries list #1681arrow-up-right

  • Data Consent: Fix field always evaluating as True in workflow conditions #1646arrow-up-right
  • Entries: Fix Workflows and Updated columns hidden with custom display fields #1655arrow-up-right

  • Settings: Fix default value leak across field type instances #1656arrow-up-right

  • Settings: Add CheckBoxList and RadioButtonList support for custom field/workflow type settings #1657arrow-up-right

  • All items detailed under release candidates for 17.2.0.

  • Remove unnecessary API calls for users without Forms section access #1648arrow-up-right
  • Fix export error when form name contains a dot #1649arrow-up-right

  • Fix form entry count not displaying on first load of dashboard #1469arrow-up-right
  • Fix Rich Text Editor (RTE) missing formatting options when changing field type #1476arrow-up-right

  • Add server-side length validation for String Data Type fields #1478arrow-up-right

  • Replace native button with styled anchor in "Copy Workflows" #1595arrow-up-right

  • Improve Danish translations #1596arrow-up-right

  • Add missing translations for multiple languages #1599arrow-up-right

  • Use uui-form-layout-item for UI consistency in form settings #1607arrow-up-right

  • Add Form reference tracking API and UI #1609arrow-up-right

  • Fix form re-initialization for dynamically injected forms #1617arrow-up-right

  • Use localized labels for Workflow stage dropdown #1612arrow-up-right

  • Fix sensitive data field showing localization key as tooltip #1633arrow-up-right

  • Fix bug where prevalue sources couldn't be saved and didn't show existing values #1597arrow-up-right
  • Path traversal and file enumeration vulnerability on Linux/macOS GHSA-hm5p-82g6-m3xharrow-up-right

  • Fix prevalue source disappearing properties #1551arrow-up-right
  • Add disabled state for unconfigured field types #1557arrow-up-right

  • Fix form picker multiple value conversion #1562arrow-up-right

  • Fix form picker multiple selection storage #1563arrow-up-right

  • Add disabled state to workflows #1566arrow-up-right

  • Fix workflow sorting #1567arrow-up-right

  • Allow sorting between workflow stages #1568arrow-up-right

  • Handle missing or deleted workflows #1570arrow-up-right

  • Workflow name field now longer #1573arrow-up-right

  • Fix Forms Theme Picker validation #1577arrow-up-right

  • Resolve migration of Form Picker and Theme Picker from v13 #1578arrow-up-right

  • Add DefaultValue property to SettingAttribute #1411arrow-up-right
  • Updated field and workflow settings handling when default values are defined #1421arrow-up-right

  • Load correct icons for all Forms tree items #1457arrow-up-right

  • Improve field alias logic and input event handling #1459arrow-up-right

  • Improve form reorder sorting experience #1482arrow-up-right

  • Fix folder creation for users with single start folder #1546arrow-up-right

  • Match delete icon to CMS delete icon #1553arrow-up-right

  • Redesigned Form designer to match CMS content type designer

  • Fix redirect on entity creation and improve save flow

  • Form Info view: improved references UI and layout

  • Update entry filter UI and hide Entries tab for new forms

  • Set Cache-Control headers of form uploads to private

  • Normalise the JavaScript form parameter to always be the native DOM element #1477arrow-up-right
  • Fix console error when using Forms.PropertyEditorUi.TextWithFieldPicker in custom FieldType or Workflow #1547arrow-up-right

  • File Upload field now includes class attribute #1495arrow-up-right

  • Fix ScrollToFormScript JavaScript not working correctly #1486arrow-up-right

  • Fix endless submit loop when reinitializing forms with umbracoFormsReinitialize #1491arrow-up-right

  • Resolve issues with missing translations in backoffice #1492arrow-up-right

  • 17.3.0-rcarrow-up-right
    17.2.0arrow-up-right
    #1383arrow-up-right
    #1635arrow-up-right
    #1636arrow-up-right
    #1382arrow-up-right
    #1464arrow-up-right
    17.1.2arrow-up-right
    #1610arrow-up-right
    17.1.1arrow-up-right
    #1590arrow-up-right
    #1591arrow-up-right
    #1592arrow-up-right
    17.1.0arrow-up-right
    17.1.0-rc2arrow-up-right
    #1490arrow-up-right
    #1549arrow-up-right
    #1550arrow-up-right
    17.1.0-rcarrow-up-right
    CMS PR #21339arrow-up-right
    #1046arrow-up-right
    #1369arrow-up-right
    17.0.3arrow-up-right
    #1556arrow-up-right
    17.0.2arrow-up-right
    #1458arrow-up-right
    #1373arrow-up-right
    #1402arrow-up-right
    17.0.0arrow-up-right
    #1414arrow-up-right
    Legacy documentation on GitHubarrow-up-right
    Umbraco Forms Package pagearrow-up-right
    Disabled reCAPTCHA field type in form designer
    @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<Umbraco.Forms.Core.Models.FormsHtmlModel>
    in your export provider class. You need to return the string you wish to write to a file. For example, you can generate a
    .csv
    (comma-separated values) file. You would implement your logic to build up a comma-separated string in the
    ExportRecordsAsync
    method.
    circle-info

    In the constructor of your provider, you need to set the following properties: Alias, FileExtension, and Icon.

    The Alias is used to construct localization keys for the export type label and description displayed in the backoffice. See Localization below for details.

    FileExtension is the extension such as zip, txt or csv of the file you will be generating and serving from the file system.

    In this example below we will create a single HTML file which takes all the submissions/entries to be displayed as a HTML report. We will do this in conjunction with a Razor partial view to help build up our HTML and thus merge it with the form submission data to generate a string of HTML.

    hashtag
    Provider Class

    hashtag
    Razor Partial View

    hashtag
    Registration

    hashtag
    Advanced Example

    This approach gives us more flexibility in creating the file we wish to serve as the exported file. We do this for the export to Excel file export provider we ship in Umbraco Forms. With this we can use a library to create the Excel file and store it in a temporary location before we send back the filepath for the browser to stream down the export file.

    In this example we will create a collection of text files, one for each submission which is then zipped up into a single file and served as the export file.

    hashtag
    Localization

    The backoffice uses localization keys to display the label and description for each export type. These keys are based on the Alias property set in the constructor:

    • Label: formProviderExportTypes_{alias}

    • Description: formProviderExportTypes_{alias}Description

    For example, an export type with Alias = "exportAsHtml" will look for the keys formProviderExportTypes_exportAsHtml and formProviderExportTypes_exportAsHtmlDescription.

    circle-info

    Without localization entries, the backoffice will display the raw localization key strings instead of the intended label and description.

    Create a JavaScript language file containing the translations:

    Register the language file with a localization manifest:

    Register the localization manifests in your entry point:

    For more details on setting up localization files, see the Localizationarrow-up-right article.

    adding a type to the provider model
    Two interfaces are used to abstract the logic for setting default fields and workflows for a form. They are IApplyDefaultFieldsBehavior and IApplyDefaultWorkflowsBehavior respectively.

    The default behaviors are defined using built-in, internal classes that implement this interface.

    You can create your own implementation of these interfaces.

    hashtag
    Example - Providing a Custom Apply Workflows Behavior

    An illustrative example, adding a custom workflow that writes to the log, is shown below.

    Firstly, the custom workflow:

    Secondly, the custom implementation of IApplyDefaultWorkflowsBehavior:

    Finally, to register the custom implementation in place of the default one:

    hashtag
    Setting a Mandatory Default Workflow

    When adding a default workflow in code, it's possible to make it mandatory, which will prevent editors from removing it from a form.

    You can see this in the example above, where the IsMandatory property of the created FormWorkflowWithTypeSettings instance is set to true.

    hashtag
    Example - Providing a Custom Apply Fields Behavior

    The following class shows the default implementation provided with Forms. You can copy this and customize it to your needs.

    Again, you will need to register your custom class, for example, in a composer with:

    @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<Umbraco.Forms.Core.Models.FormsHtmlModel>
    
    @{
    	//This is an example email template where you can use Razor Views to send HTML emails
    
    	//You can use Umbraco.Content & Umbraco.Media etc to use Images & content from your site
    	//directly in your email templates too
    
    	//Strongly Typed
    	//@Model.GetValue("aliasFormField")
    	//@foreach (var color in Model.GetValues("checkboxField")){}
    
    
    	//Images need to be absolute - so fetching domain to prefix with images
    	var siteDomain = Context.Request.Scheme + "://" + Context.Request.Host;
    	var assetUrl = siteDomain + "/App_Plugins/UmbracoForms/assets/Email-Example";
    
    }
    <!DOCTYPE html>
    <html>
    <head>
    	<title></title>
    	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    	<meta name="viewport" content="width=device-width, initial-scale=1">
    	<meta http-equiv="X-UA-Compatible" content="IE=edge" />
    	<style type="text/css">
    
    		/* CLIENT-SPECIFIC STYLES */
    		body, table, td, a {
    			-webkit-text-size-adjust: 100%;
    			-ms-text-size-adjust: 100%;
    		}
    
    		table, td {
    			mso-table-lspace: 0pt;
    			mso-table-rspace: 0pt;
    		}
    
    		img {
    			-ms-interpolation-mode: bicubic;
    		}
    
    		/* RESET STYLES */
    		img {
    			border: 0;
    			height: auto;
    			line-height: 100%;
    			outline: none;
    			text-decoration: none;
    		}
    
    		table {
    			border-collapse: collapse !important;
    		}
    
    		body {
    			height: 100% !important;
    			margin: 0 !important;
    			padding: 0 !important;
    			width: 100% !important;
    		}
    
    		/* iOS BLUE LINKS */
    		a[x-apple-data-detectors] {
    			color: inherit !important;
    			text-decoration: none !important;
    			font-size: inherit !important;
    			font-family: inherit !important;
    			font-weight: inherit !important;
    			line-height: inherit !important;
    		}
    
    		/* MOBILE STYLES */
    		@@media screen and (max-width:600px) {
    			h1 {
    				font-size: 32px !important;
    				line-height: 32px !important;
    			}
    		}
    
    		/* ANDROID CENTER FIX */
    		div[style*="margin: 16px 0;"] {
    			margin: 0 !important;
    		}
    	</style>
    </head>
    <body style="background-color: #f4f4f4; margin: 0 !important; padding: 0 !important;">
    	<table border="0" cellpadding="0" cellspacing="0" width="100%">
    		<!-- HERO -->
    		<tr>
    			<td align="center" style="padding: 40px 10px 0px 10px;">
    				<!--[if (gte mso 9)|(IE)]>
    					<table align="center" border="0" cellspacing="0" cellpadding="0" width="600">
    					<tr>
    					<td align="center" valign="top" width="600">
    				<![endif]-->
    				<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
    					<tr>
    						<td bgcolor="#ffffff" align="center" valign="top" style="padding: 40px 20px 20px 20px; color: #000000; font-family: Helvetica, Arial, sans-serif; font-size: 36px; font-weight: 900; line-height: 48px;">
    							<h1 style="font-size: 36px; font-weight: 900; margin: 0;">Submission for @Model.FormName</h1>
    						</td>
    					</tr>
    				</table>
    				<!--[if (gte mso 9)|(IE)]>
    					</td>
    					</tr>
    					</table>
    				<![endif]-->
    			</td>
    		</tr>
    
    		<!-- COPY BLOCK -->
    		<tr>
    			<td bgcolor="#F3F3F5" align="center" style="padding: 0px 10px 40px 10px;">
    				<!--[if (gte mso 9)|(IE)]>
    					<table align="center" border="0" cellspacing="0" cellpadding="0" width="600">
    					<tr>
    					<td align="center" valign="top" width="600">
    				<![endif]-->
    				<table border="0" cellpadding="0" cellspacing="0" width="100%" style="max-width: 600px;">
    					<!-- HEADER COPY -->
    					@if (Model.HeaderHtml is not null)
    					{
    						<tr>
    							<td bgcolor="#ffffff" align="left" style="padding: 20px 30px 40px 30px; color: #303033; font-family: Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 1.6em;">
    								@Model.HeaderHtml
    							</td>
    						</tr>
    					}
    
    					<!-- BODY COPY -->
    					@if (Model.BodyHtml is not null)
    					{
    						<tr>
    							<td bgcolor="#ffffff" align="left" style="padding: 20px 30px 40px 30px; color: #303033; font-family: Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 1.6em;">
    								@Model.BodyHtml
    							</td>
    						</tr>
    					}
    
    					<!-- FORM FIELDS HEADING -->
    					<tr>
    						<td bgcolor="#ffffff" align="left" style="padding: 40px 30px 0px 30px; color: #000000; font-family: Helvetica, Arial, sans-serif; line-height: 25px;">
    							<h2 style="font-size: 24px; font-weight: 700; margin: 0;">Form Results</h2>
    						</td>
    					</tr>
    
    					<!-- FORM FIELDS -->
    					<tr>
    						<td bgcolor="#ffffff" align="left" style="padding: 20px 30px 40px 30px; color: #303033; font-family: Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;">
    
    							@{
    								string[] ignoreFields = new string[]
    								{
    									"FieldType.Recaptcha2.cshtml",
    									"FieldType.Recaptcha3.cshtml",
    									"FieldType.RichText.cshtml",
    									"FieldType.Text.cshtml"
    								};
    							}
    
    							@foreach (var field in Model.Fields.Where(x => ignoreFields.Contains(x.FieldType) == false))
    							{
    								<h4 style="font-weight: 700; margin: 0; color: #000000;">@field.Name</h4>
    
    								<p style="margin-top: 0;">
    									@switch (field.FieldType)
    									{
    										case "FieldType.FileUpload.cshtml":
    											var uploadCount = 0;
    											foreach (var fileUploadValue in field.GetValues())
    											{
    												if (fileUploadValue != null && !string.IsNullOrEmpty(fileUploadValue.ToString()))
    												{
    													uploadCount++;
    												}
    											}
    
    											if (uploadCount > 0)
    											{
    												<span>@uploadCount file@(uploadCount == 1 ? string.Empty : "s") uploaded</span>
    											}
    
    											break;
    
    										case "FieldType.DatePicker.cshtml":
    											var datePickerValue = field.GetValue();
    											if (datePickerValue != null && !string.IsNullOrEmpty(datePickerValue.ToString()))
    											{
    												DateTime dt;
    												var dateValid = DateTime.TryParse(datePickerValue != null ? datePickerValue.ToString() : string.Empty, out dt);
    												var dateStr = dateValid ? dt.ToString("f") : "";
    												@dateStr
    											}
    											break;
    
    										default:
    											var values = field.GetValues();
    											if (values != null)
    											{
    												foreach (var value in values)
    												{
    													if (value != null)
    													{
    														@(value is string strValue ? strValue.ApplyPrevalueCaptions(field.Id, Model.PrevalueMaps) : value)
    
    														<br />
    													}
    												}
    											}
    											break;
    									}
    								</p>
    							}
    
    						</td>
    					</tr>
    
    					<!-- FOOTER COPY -->
    					@if (Model.FooterHtml is not null)
    					{
    						<tr>
    							<td bgcolor="#ffffff" align="left" style="padding: 20px 30px 40px 30px; color: #303033; font-family: Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 1.6em;">
    								@Model.FooterHtml
    							</td>
    						</tr>
    					}
    				</table>
    				<!--[if (gte mso 9)|(IE)]>
    					</td>
    					</tr>
    					</table>
    				<![endif]-->
    			</td>
    		</tr>
    
    	</table>
    </body>
    </html>
    using System;
    using System.Threading.Tasks;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Models;
    using Umbraco.Forms.Core.Searchers;
    using Umbraco.Forms.Web.Helpers;
    
    namespace MyFormsExtensions
    {
        public class ExportToHtml : ExportType
        {
            private readonly IHttpContextAccessor _httpContextAccessor;
            private readonly IFormRecordSearcher _formRecordSearcher;
    
            public ExportToHtml(
                IHttpContextAccessor httpContextAccessor,
                IFormRecordSearcher formRecordSearcher)
            {
                _httpContextAccessor = httpContextAccessor;
                _formRecordSearcher = formRecordSearcher;
    
                Name = "Export as HTML";
                Description = "Export entries as a single HTML report";
                Alias = "exportAsHtml";
                Id = new Guid("4117D352-FB41-4A4C-96F5-F6EF35B384D2");
                FileExtension = "html";
                Icon = "icon-article";
            }
    
            public override Task<string> ExportRecordsAsync(Guid formId, RecordExportFilter filter)
            {
                var view = "~/Views/Partials/Forms/Export/html-report.cshtml";
                EntrySearchResultCollection model = _formRecordSearcher.QueryDataBase(filter);
                return Task.FromResult(
                    ViewHelper.RenderPartialViewToString(_httpContextAccessor.GetRequiredHttpContext(), view, model));
            }
        }
    }
    @model Umbraco.Forms.Core.Searchers.EntrySearchResultCollection
    
    @{
        var submissions = Model.Results.ToList();
        var schemaItems = Model.Schema.ToList();
    }
    
    <h1>Form Submissions</h1>
    
    @foreach (var submission in submissions)
    {
        var values = submission.Fields.ToList();
    
        for (int i = 0; i < schemaItems.Count; i++)
        {
          <strong>@schemaItems[i].Name</strong> @values[i].Value
          <br />
        }
    
        <hr />
    }
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Forms.Core.Providers.Extensions;
    using Umbraco.Forms.TestSite.Business.ExportTypes;
    
    namespace MyFormsExtensions
    {
        public class TestComposer : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.FormsExporters().Add<ExportToHtml>();
            }
        }
    }
    using System;
    using System.IO;
    using System.IO.Compression;
    using System.Linq;
    using System.Threading.Tasks;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Models;
    using Umbraco.Forms.Core.Searchers;
    
    namespace MyFormsExtensions
    {
        public class ExportToTextFiles : ExportType
        {
            private readonly IFormRecordSearcher _formRecordSearcher;
    
            public ExportToTextFiles(IFormRecordSearcher formRecordSearcher)
            {
                _formRecordSearcher = formRecordSearcher;
    
                Name = "Export as text files";
                Description = "Export entries as text files inside a zip file";
                Alias = "exportAsTextFiles";
                Id = new Guid("171CABC9-2207-4575-83D5-2A77E824D5DB");
                FileExtension = "zip";
                Icon = "icon-zip";
            }
    
            /// <summary>
            /// We do not implement this method from the interface
            /// As this method is called from ExportToFile that we also override here & is expecting the file contents as a string to be written as a stream to a file
            /// Which would be OK if we were creating a CSV or a single based file that can have a simple string written as a string such as one large HTML report or XML file perhaps
            /// </summary>
            public override Task<string> ExportRecordsAsync(Guid formId, RecordExportFilter filter) => throw new NotImplementedException();
    
            /// <summary>
            /// This gives us greater control of the export process
            /// </summary>
            /// <param name="formId">The form identifier.</param>
            /// <param name="filter">
            /// This filter contains the date range & other search parameters to limit the entries we are exporting
            /// </param>
            /// <param name="filepath">
            /// The filepath that the export file is expecting to be served from
            /// So ensure that the zip of text files is saved at this location
            /// </param>
            /// <returns>The final file path to serve up as the export - this is unlikely to change through the export logic</returns>
            public override Task<string> ExportToFileAsync(Guid formId, RecordExportFilter filter, string filepath)
            {
                // Before Save - Check Path, Directory & Previous File export does not exist
                string pathToSaveZipFile = filepath;
    
                // Get the directory
                var dir = Path.GetDirectoryName(filepath);
                var tempFileDir = Path.Combine(dir, "text-files");
    
                // If the path does not end with our file extension, ensure it's added
                if (pathToSaveZipFile.EndsWith("." + FileExtension) == false)
                {
                    pathToSaveZipFile += "." + FileExtension;
                }
    
                // Check that the directory where we will save the ZIP file temporarily exists
                // If not just create it
                if (Directory.Exists(tempFileDir) == false)
                {
                    Directory.CreateDirectory(tempFileDir);
                }
    
                // Check if the zip file exists already - if so delete it, as we have a new update
                if (File.Exists(pathToSaveZipFile))
                {
                    File.Delete(pathToSaveZipFile);
                }
    
                // Query the DB for submissions to export based on the filter
                EntrySearchResultCollection submissions = _formRecordSearcher.QueryDataBase(filter);
    
                // Get the schema objects to a list so we can get items using position index
                var schemaItems = submissions.schema.ToList();
    
                // We will use this to store our contents of our file to save as a text file
                var fileContents = string.Empty;
    
                // For each submission we have build up a string to save to a text file
                foreach (EntrySearchResult submission in submissions.Results)
                {
                    // The submitted data for the form submission
                    var submissionData = submission.Fields.ToList();
    
                    // For loop to match the schema position to the submission data
                    for (int i = 0; i < schemaItems.Count; i++)
                    {
                        // Concat a string of the name of the field & its stored data
                        fileContents += schemaItems[i].Name + ": " + submissionData[i] + Environment.NewLine;
                    }
    
                    // Now save the contents to a text file
                    // Base it on the format of the record submission unique id
                    var textFileName = Path.Combine(tempFileDir, submission.UniqueId + ".txt");
                    File.WriteAllText(textFileName, fileContents);
    
                    // Reset fileContents to be empty again
                    fileContents = string.Empty;
                }
    
                // Now we have a temp folder full of text files
                // Generate a zip file containing them & save that
                ZipFile.CreateFromDirectory(tempFileDir, pathToSaveZipFile);
    
                // Tidy up after ourselves & delete the temp folder of text files
                if (Directory.Exists(tempFileDir))
                {
                    Directory.Delete(tempFileDir, true);
                }
    
                // Return the path where we saved the zip file containing the text files
                return Task.FromResult(pathToSaveZipFile);
            }
        }
    }
    import type { UmbLocalizationDictionary } from "@umbraco-cms/backoffice/localization-api";
    
    export default {
      formProviderExportTypes: {
        exportAsHtml: "Export as HTML",
        exportAsHtmlDescription: "Export entries as a single HTML report",
      },
    } as UmbLocalizationDictionary;
    import type { ManifestLocalization } from '@umbraco-cms/backoffice/localization';
    
    const localizationManifests: Array<ManifestLocalization> = [
      {
        type: "localization",
        alias: "My.Localization.En_US",
        weight: -100,
        name: "English (US)",
        meta: {
          culture: "en-us",
        },
        js: () => import("./en-us.js"),
      },
    ];
    
    export const manifests = [...localizationManifests];
    import { manifests as localizationManifests } from "./lang/manifests.js";
    
    export const onInit = async (host, extensionRegistry) => {
      extensionRegistry.registerMany(localizationManifests);
    };
    using System;
    using System.Collections.Generic;
    using Umbraco.Core.Composing;
    using Umbraco.Core.Logging;
    using Umbraco.Forms.Core.Attributes;
    using Umbraco.Forms.Core.Enums;
    using Umbraco.Forms.Core.Persistence.Dtos;
    
    namespace MyNamespace
    {
        public class LogMessageWorkflow : WorkflowType
        {
            public const string LogMessageWorkflowId = "7ca500a7-cb34-4a82-8ae9-2acac777382d";
            private readonly ILogger<LogMessageWorkflow> _logger;
    
            public LogMessageWorkflow(ILogger<LogMessageWorkflow> logger)
            {
                Id = new Guid(LogMessageWorkflowId);
                Name = "Test Workflow";
                Description = "A test workflow that writes a log line";
                Icon = "icon-edit";
    
                _logger = logger;
            }
    
            [Setting("Message", Description = "The log message to write", View = "Umb.PropertyEditorUi.TextBox")]
            public string Message { get; set; }
    
            public override List<Exception> ValidateSettings()
            {
                var exs = new List<Exception>();
                if (string.IsNullOrEmpty(Message))
                {
                    exs.Add(new Exception("'Message' setting has not been set"));
                }
    
                return exs;
            }
    
            public override WorkflowExecutionStatus Execute(WorkflowExecutionContext context)
            {
                _logger.LogInformation($"'{Message}' written at {DateTime.Now}");
                return WorkflowExecutionStatus.Completed;
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Umbraco.Cms.Core.Hosting;
    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Enums;
    using Umbraco.Forms.Core.Providers;
    using Umbraco.Forms.Web.Behaviors;
    using Umbraco.Forms.Web.Models.Backoffice;
    
    namespace MyNamespace
    {
        public class CustomApplyDefaultWorkflowsBehavior : IApplyDefaultWorkflowsBehavior
        {
            private readonly WorkflowCollection _workflowCollection;
            private readonly IHostingEnvironment _hostingEnvironment;
    
            public CustomApplyDefaultWorkflowsBehavior(
                WorkflowCollection workflowCollection, IHostingEnvironment hostingEnvironment)
            {
                _workflowCollection = workflowCollection;
                _hostingEnvironment = hostingEnvironment;
            }
    
            public void ApplyDefaultWorkflows(FormDesign form)
            {
                // Retrieve the type of the default workflow to add.
                WorkflowType testWorkflowType = _workflowCollection[new Guid(LogMessageWorkflow.LogMessageWorkflowId)];
    
                // Create a workflow object based on the workflow type.
                var defaultWorkflow = new FormWorkflowWithTypeSettings
                {
                    Id = Guid.Empty,
                    Name = "Log a message",
                    Active = true,
                    IncludeSensitiveData = IncludeSensitiveData.False,
                    SortOrder = 1,
                    WorkflowTypeId = testWorkflowType.Id,
                    WorkflowTypeName = testWorkflowType.Name,
                    WorkflowTypeDescription = testWorkflowType.Description,
                    WorkflowTypeGroup = testWorkflowType.Group,
                    WorkflowTypeIcon = testWorkflowType.Icon,
    
                    // Optionally set the default workflow to be mandatory (which means editors won't be able to remove it
                    // via the back-office user interface).
                    IsMandatory = true
                };
    
                // Retrieve the settings from the type.
                Dictionary<string, Core.Attributes.Setting> workflowTypeSettings = testWorkflowType.Settings();
    
                // Create a collection for the specific settings to be applied to the workflow.
                // Populate with the setting details from the type.
                var workflowSettings = new List<SettingWithValue>();
                foreach (KeyValuePair<string, Core.Attributes.Setting> setting in workflowTypeSettings)
                {
                    Core.Attributes.Setting settingItem = setting.Value;
    
                    var settingItemToAdd = new SettingWithValue
                    {
                        Name = settingItem.Name,
                        Alias = settingItem.Alias,
                        Description = settingItem.Description,
                        Prevalues = settingItem.GetPreValues(),
                        View = _hostingEnvironment.ToAbsolute(settingItem.GetSettingView()),
                        Value = string.Empty
                    };
    
                    workflowSettings.Add(settingItemToAdd);
                }
    
                // For each setting, provide a value for the workflow instance (in this example, we only have one).
                SettingWithValue messageSetting = workflowSettings.SingleOrDefault(x => x.Alias == "Message");
                if (messageSetting != null)
                {
                    messageSetting.Value = "A test log message";
                }
    
                // Apply the settings to the workflow.
                defaultWorkflow.Settings = workflowSettings;
    
                // Associate the workflow with the appropriate form submission event.
                form.FormWorkflows.OnSubmit.Add(defaultWorkflow);
            }
        }
    }
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Cms.Core.DependencyInjection;
    using Umbraco.Extensions;
    using Umbraco.Forms.Core.Providers;
    using Umbraco.Forms.Testsite.Business.Workflows;
    using Umbraco.Forms.Web.Behaviors;
    
    namespace MyNamespace
    {
        public class TestSiteComposer : IComposer
        {
            public void Compose(IUmbracoBuilder builder)
            {
                builder.WithCollectionBuilder<WorkflowCollectionBuilder>()
                    .Add<LogMessageWorkflow>();
    
                builder.Services.AddUnique<IApplyDefaultWorkflowsBehavior, CustomApplyDefaultWorkflowsBehavior>();
            }
        }
    }
    using Microsoft.Extensions.Options;
    using Umbraco.Forms.Core.Configuration;
    using Umbraco.Forms.Core.Models;
    using Umbraco.Forms.Web.Extensions;
    using Umbraco.Forms.Web.Models.Backoffice;
    
    namespace Umbraco.Forms.Web.Behaviors
    {
        internal class CustomApplyDefaultFieldsBehavior : IApplyDefaultFieldsBehavior
        {
            private readonly FormDesignSettings _formDesignSettings;
    
            public CustomApplyDefaultFieldsBehavior(IOptions<FormDesignSettings> formDesignSettings) =>
                _formDesignSettings = formDesignSettings.Value;
    
            public virtual void ApplyDefaultFields(FormDesign form)
            {
                // Add one page as a starting point.
                var page = new Page();
                form.Pages.Add(page);
    
                // Add one empty fieldset to the page to start with.
                var fieldset = new FieldSet
                {
                    Id = Guid.NewGuid()
                };
                page.FieldSets.Add(fieldset);
    
                // Add one full-width (12cols) container/row to the fieldset.
                var container = new FieldsetContainer
                {
                    Width = 12
                };
                fieldset.Containers.Add(container);
    
                // As all forms default to having StoreRecordsLocally we need to add the data consent field to the the form
                // (unless this feature has been explicitly disabled).
                if (_formDesignSettings.DisableAutomaticAdditionOfDataConsentField)
                {
                    return;
                }
    
                container.AddDataConsentField(_formDesignSettings, _fieldCollection);
    
                // Add any further fields you require.
            }
        }
    }
    builder.Services.AddUnique<IApplyDefaultFieldsBehavior, CustomApplyDefaultFieldsBehavior>();

    Security

    How to secure access to Umbraco Forms data and functionality.

    Umbraco Forms has a backoffice security model integrated with Umbraco users. Details are managed in the Forms section of the backoffice, within a tree named Security.

    hashtag
    User-based permissions

    Within the Forms > Security tree, each user with a backoffice account is listed. Clicking on a user allows each functional permission to be set:

    • Manage Forms - user can create and edit form definitions

    • View Entries - user can view the submitted entries

    • Edit Entries - user can edit the submitted entries

    • Delete entries - user can delete the submitted entries

    • Manage Workflows - user can create and edit workflow items

    • Manage Datasources - user can create and edit datasource definitions

    • Manage Prevalue Sources - user can create and edit prevalue source definitions

    For further control, each form is listed and the user can be granted or denied access to each as appropriate.

    As new forms are created, users will automatically be granted access to them, unless the configuration setting DefaultUserAccessToNewForms has been set to a value of Deny.

    hashtag
    Start Folders

    When form definitions are configured for storage in the database, it allows for the creation of folders to group forms within. It's also possible to define one or more start folders for a user. This is done in order to limit their access to a subset of the forms available.

    If no start folders are selected, the user will be able to access all forms in the backoffice according to their permissions.

    If a single start folder is selected, that will act as the root of the tree view of forms. The user will have access to all folders and forms below that selected folder.

    If more than one start folder is selected, they will appear underneath the root of the tree view of forms. The user will have access to only those folders and their descendant folders and forms.

    hashtag
    User group based permissions

    A new model was introduced allowing for the management of permissions at the level of user groups. Particularly for installations with a large number of users, we expect this to be a more useful setup and require less ongoing administration.

    When user groups are involved in permissions, access to a particular resource or feature is determined by the following:

    • If the user has a specific user permission set, it is used in preference to anything set on the user groups they are a part of.

    • If the user doesn't have a specific user permission set, they are granted access if at least one of the user groups they are part of has access.

    To enable the feature, it's necessary to update the ManageSecurityWithUserGroups configuration setting to true.

    With that in place the Form Security tree divides into three sub-trees:

    • Under Group Permissions, each user group is listed and the same settings as described above for individual users can be set here.

    • Under User Permissions, each user that has a specific user permission record is listed and can be managed. Records for users can be created or deleted via the tree's action menu.

    As new forms are created, user groups with aliases listed in the GrantAccessToNewFormsForUserGroups configuration setting will be automatically given access. For example, with a value of admin, editor, the built-in Administrators and Editors groups would have access.

    hashtag
    Start folders for user groups

    Start folders are enabled for User Groups. They work in a similar way as the group based permissions described above:

    • If the user has a specific user permission set, it is used in preference to anything set on the user groups they are a part of.

      • This means if the user has no start folders defined and the groups they are part of do, they will have access to the root of the Forms tree and be able to access all folders and Forms.

    • If the user doesn't have a specific user permission set, they are granted access to all the unique folders the groups they are part of have access to.

    hashtag
    Migrating to user group-based permissions

    In introducing the user group based permissions, we've taken care to ensure a migration path. This is available for those existing installations running on older versions of Umbraco Forms. In that situation, we'd recommend the following approach.

    • Upgrade to Umbraco 9.3.

    • At this stage nothing will have changed in terms of the permissions model in use.

    • Set the ManageSecurityWithUserGroups configuration value to true and the GrantAccessToNewFormsForUserGroups

    hashtag
    Handling Sensitive Data in Umbraco Forms

    Marking fields and properties as sensitive will hide the data in those fields for backoffice users that are not privy to the data. Built-in features are available to help you secure sensitive information. For more information, see the article.

    The following sections covers how to grant or deny access to sensitive data for specific users and how to mark form questions as sensitive.

    hashtag
    Assigning Users to the Sensitive Data Group

    To allow users to view and handle sensitive data in Umbraco Forms, you must assign them to the Sensitive Data user group:

    1. Navigate to the Users section in the Umbraco Backoffice.

    2. Select the user you want to grant access to.

    3. Click Choose in the Groups field under the Assign access section.

    hashtag
    Marking Questions in Forms as Sensitive

    Once the users are set up with the appropriate permissions, the next step is to identify which form fields should be marked as sensitive.

    Marking a field as sensitive ensures that only authorized users in the Sensitive Data user group can access data from these fields.

    To mark questions as sensitive, follow these steps:

    1. Navigate to the Forms section in the Umbraco Backoffice.

    2. Open the form you wish to configure (for example: Contact Form).

    3. Click on the cogwheel icon next to the form field you want to secure.

    1. Click Submit.

    2. Click Save.

    Forms Provider Type Details

    Provides details of the built-in provider types available with Umbraco Forms

    This page provides some details of the provider types available in Umbraco Forms.

    The intention is to be able to make available details such as IDs, aliases and property names, that may be necessary when configuring the product.

    hashtag
    Field Types

    chevron-rightCheckbox
    hashtag

    ID: D5C0C390-AE9A-11DE-A69E-666455D89593

    Alias: checkbox

    Settings:

    • Caption

    • DefaultValue

    • ShowLabel

    chevron-rightData Consenthashtag

    ID: A72C9DF9-3847-47CF-AFB8-B86773FD12CD

    Alias: dataConsent

    Settings:

    • AcceptCopy

    • ShowLabel

    chevron-rightDatehashtag

    ID: F8B4C3B8-AF28-11DE-9DD8-EF5956D89593

    Alias: date

    Settings:

    • Placeholder

    chevron-rightDropdown Listhashtag

    ID: 0DD29D42-A6A5-11DE-A2F2-222256D89593

    Alias: dropdown

    Settings:

    • DefaultValue

    • AllowMultipleSelections

    • ShowLabel

    • AutocompleteAttribute

    • SelectPrompt

    chevron-rightFile Uploadhashtag

    ID: 84A17CF8-B711-46a6-9840-0E4A072AD000

    Alias: fileUpload

    Settings:

    • SelectedFilesListHeading

    chevron-rightLong Answerhashtag

    ID: 023F09AC-1445-4bcb-B8FA-AB49F33BD046

    Alias: longAnswer

    Settings:

    • DefaultValue

    • Placeholder

    • ShowLabel

    • AutocompleteAttribute

    • NumberOfRows

    • MaximumLength

    chevron-rightHidden Fieldhashtag

    ID: DA206CAE-1C52-434E-B21A-4A7C198AF877

    Alias: hidden

    Settings:

    • DefaultValue

    chevron-rightMultiple Choicehashtag

    ID: FAB43F20-A6BF-11DE-A28F-9B5755D89593

    Alias: multipleChoice

    Settings:

    • DisplayLayout

    • DefaultValue

    • ShowLabel

    chevron-rightPasswordhashtag

    ID: FB37BC60-D41E-11DE-AEAE-37C155D89593

    Alias: password

    Settings:

    • Placeholder

    chevron-rightreCAPTCHA 2hashtag

    ID: B69DEAEB-ED75-4DC9-BFB8-D036BF9D3730

    Alias: recaptcha2

    Settings:

    • Theme

    • Size

    • ErrorMessage

    chevron-rightreCAPTCHA 3hashtag

    ID: 663AA19B-423D-4F38-A1D6-C840C926EF86

    Alias: recaptcha3

    Settings:

    • ScoreThreshold

    • ErrorMessage

    • SaveScore

    chevron-rightreCAPTCHA Enterprisehashtag

    ID: 1BAB78CB-52B1-495C-BBC2-A46540642828

    Alias: recaptchaEnterprise

    Settings:

    • ScoreThreshold

    • ErrorMessage

    • SaveScore

    chevron-rightRich Texthashtag

    ID: 1F8D45F8-76E6-4550-A0F5-9637B8454619

    Alias: richText

    Settings:

    • Html

    • ShowLabel

    chevron-rightSingle Choicehashtag

    ID: 903DF9B0-A78C-11DE-9FC1-DB7A56D89593

    Alias: singleChoice

    Settings:

    • DisplayLayout

    • DefaultValue

    • ShowLabel

    chevron-rightShort Answerhashtag

    ID: 3F92E01B-29E2-4a30-BF33-9DF5580ED52C

    Alias: shortAnswer

    Settings:

    • DefaultValue

    • Placeholder

    • ShowLabel

    • MaximumLength

    • FieldType

    • AutocompleteAttribute

    chevron-rightTitle and Descriptionhashtag

    ID: e3fbf6c4-f46c-495e-aff8-4b3c227b4a98

    Alias: titleAndDescription

    Settings:

    • CaptionTag

    • Caption

    • BodyText

    • ShowLabel

    hashtag
    Workflow Types

    chevron-rightChange Record Statehashtag

    ID: 4C40A092-0CB5-481d-96A7-A02D8E7CDB2F

    Alias: changeRecordState

    Settings:

    • Words

    • Action

    chevron-rightPost as XMLhashtag

    ID: 470EEB3A-CB15-4b08-9FC0-A2F091583332

    Alias: postAsXml

    Settings:

    • Url

    • Method

    • XsltFile

    • Fields

    • Username

    • Password

    chevron-rightSave As Umbraco Content Nodehashtag

    ID: 89FB1E31-9F36-4e08-9D1B-AF1180D340DB

    Alias: saveAsUmbracoContentNode

    Settings:

    • Fields

    • Publish

    • RootNode

    chevron-rightSave As XML Filehashtag

    ID: 9CC5854D-61A2-48f6-9F4A-8F3BDFAFB521

    Alias: saveAsAnXmlFile

    Settings:

    • Path

    • Extension

    • XsltFile

    chevron-rightSend Emailhashtag

    ID: E96BADD7-05BE-4978-B8D9-B3D733DE70A5

    Alias: sendEmail

    Settings:

    • Email

    • CcEmail

    • BccEmail

    • SenderEmail

    • ReplyToEmail

    • Subject

    • Message

    • Attachment

    chevron-rightSend Email With Razor Templatehashtag

    ID: 17c61629-d984-4e86-b43b-a8407b3efea9

    Alias: sendEmailWithRazorTemplate

    Settings:

    • Email

    • CcEmail

    • BccEmail

    • SenderEmail

    • ReplyToEmail

    • Subject

    • RazorViewFilePath

    • Attachment

    chevron-rightSend Email With Extensible Stylesheet Language Transformations (XSLT) Templatehashtag

    ID: 616edfeb-badf-414b-89dc-d8655eb85998

    Alias: sendEmailWithXsltTemplate

    Settings:

    • Email

    • CcEmail

    • BccEmail

    • SenderEmail

    • ReplyToEmail

    • Subject

    • XsltFile

    chevron-rightSend Form To URLhashtag

    ID: FD02C929-4E7D-4f90-B9FA-13D074A76688

    Alias: sendFormToUrl

    Settings:

    • Url

    • Method

    • StandardFields

    • Fields

    • Username

    • Password

    chevron-rightSlackhashtag

    ID: bc52ab28-d3ff-42ee-af75-a5d49be83040

    Alias: slack

    Settings:

    • WebhookUrl

    chevron-rightSlack (Legacy)hashtag

    ID: ccbfb0d5-adaa-4729-8b4c-4bb439dc0202

    Alias: slackLegacy

    Settings:

    • Token

    • Channel

    • Username

    • AvatarUrl

    hashtag
    Prevalue Source Types

    chevron-rightDatasourcehashtag

    ID: cc9f9b2a-a746-11de-9e17-681b56d89593

    Alias: dataSource

    chevron-rightGet Values From Text Filehashtag

    ID: 35C2053E-CBF7-4793-B27C-6E97B7671A2D

    Alias: getValuesFromTextFile

    Settings:

    • TextFile

    chevron-rightSQL Databasehashtag

    ID: F1F5BD4D-E6AE-44ed-86CB-97661E4660B2

    Alias: sqlDatabase

    Settings:

    • Connection

    • ConnectionString

    • Table

    • KeyColumn

    • ValueColumn

    • CaptionColumn

    chevron-rightUmbraco Datatype Prevalueshashtag

    ID: EA773CAF-FEF2-491B-B5B7-6A3552B1A0E2

    Alias: umbracoDataTypePreValues

    Settings:

    • DataTypeId

    chevron-rightUmbraco Documentshashtag

    ID: de996870-c45a-11de-8a39-0800200c9a66

    Alias: umbracoDocuments

    Settings:

    • RootNode

    • UseCurrentPage

    • DocType

    • ValueField

    • CaptionField

    • ListGrandChildren

    • OrderBy

    hashtag
    Data Source Types

    chevron-rightSQL Databasehashtag

    ID: F19506F3-EFEA-4b13-A308-89348F69DF91

    Alias: sqlDatabase

    Settings:

    • Connection

    • Table

    If they are part of any group that has access to the forms section, permission to manage forms and no start folders defined, they will have access to the root of the Forms tree and be able to access all folders and Forms.

    as appropriate for your setup.
  • Via the Users > Form Security section, set the required permissions on each user group.

  • Again at this point nothing will have changed with regard the effective permissions for each user, as they will currently all have an existing user permission record.

  • Via Users > Form Security > User permissions, delete the permission records for each user.

  • The effective permissions for each user will now be derived from their user groups.

  • If you have any exceptions - where a particular user needs a particular combination of permissions that you can't or don't want to provide via the user groups - it's always possible to re-create a user permission record that will take precedence over the group based permissions.

  • Select Sensitive Data from the list of User Groups.
  • Click Submit.

  • Click Save.

  • Enable the Sensitive data setting for the field.
    Sensitive dataarrow-up-right
    Start folders
    User group permissions
    Assigning Users to the Sensitive Data Group
    Mark Question as Sensitive
    Sensitive Data on Field

    Creating a Contact Form

    In this tutorial, we'll look at creating a Contact Form using Umbraco Forms. It will take you through the process of creating a Contact Form and cover all the different components involved in building the form.

    You can use a Contact Form on your website to allow a visitors to send you a message. Having a Contact Form on your website allows you to keep track of potential customer queries and possibly generate leads via email communication.

    hashtag
    Video Tutorial

    hashtag
    Step 1: Configure the Document Types

    The first step in this tutorial is to configure the Document Types that will be used to show the Contact Form on your website.

    hashtag
    Creating a Composition

    We'll start off by creating a Composition. A Composition is a stand-alone Document Type, that you can reuse on other Document Types. By creating a Composition, we are not duplicating the same properties on multiple Document Types. This is helpful when we want to use the same set of properties on multiple Document Types.

    To create a Composition, follow these steps:

    1. Go to Settings in the Umbraco Backoffice.

    2. Expand the Document Types folder in the Settings tree.

    3. Select ... next to the Compositions folder.

    4. Click Create.

    5. Select Document Type.

    6. Enter a Name for the Composition- let's call it Title Box.

    7. Add the following fields with the respective specifications:

      Group
      Field Name
      Alias
      Data Type
    8. Click Save to save the Composition.

    hashtag
    Creating a Contact Us Document Type with Template

    Next, we will create a Document type with template. A Document Type contains different properties for holding different types of content. The Document Type we create here will be the one used for creating the content page that will hold our Contact Form.

    To create a Contact Us Document Type, follow these steps:

    1. Go to Settings in the Umbraco Backoffice.

    2. Select ... next to the Document Types folder.

    3. Click Create.

    4. Select Document Type with Template.

    5. Enter a Name for the Document Type- let's call it Contact Us.

    6. Select Compositions in the top-right corner.

    7. Select Title Box.

    8. Click Submit.

    9. Add the following fields with the respective specifications:

      Group
      Field Name
      Alias
      Data Type
    10. Click Save.

    hashtag
    Updating the Document Type Permission

    In the following we will update the Document Type permissions to specifically add child nodes under the root content node.

    To update the Contact Us Document Type permissions, follow these steps:

    1. Navigate to the Document Type used for the root content node on your website, in this case Home page.

    2. Go to the Structure tab.

    3. Select Choose in the Allowed child node types section.

    4. Select the Contact Us page.

    5. Click Choose.

    6. Click Save.

    hashtag
    Step 2: Prepare the Content Node

    This step takes you through creating the content node for your Contact Form. The content node uses the Document Type and Template to serve up an HTML page to web visitors.

    To add the content node, follow these steps:

    1. Go to Content in the Umbraco Backoffice.

    2. Select ... next to the Home Page.

    3. Click Create.

    4. Select Contact Us.

    5. Enter the name for the content node. let's call it Contact Us.

    6. Enter a Title, Subtitle, and Body Text value. These can always be updated at a later point.

    7. Click Save or Save and Publish.

    hashtag
    Step 3: Creating the Contact Form

    In this step, we will create the Contact Form using Umbraco Forms.

    To create a form, follow these steps:

    1. Go to the Forms section in the Umbraco Backoffice.

    2. Click ... next to the Forms folder.

    3. Click Create.

    4. Select New Form....

    5. Enter a Name for the Form. Let's call it Contact Us.

    6. [Optional] Enter a Page Name and Group Name for the Data Consent statement. Let's call it Data Consent.

    7. Click Add new group. Let's call it Information.

    8. Select Add Question to add a new field.

    9. Enter the following details:

      Field Name
      Value
    10. Click Submit.

    11. Repeat steps 8-10 to add the following fields:

      Field Name
      Value
    12. Enable Conditions in the Enter your phone number field.

    13. Click Add Condition.

    14. Select How should we contact you? from the dropwdown.

    15. Select phone in the value field.

    16. Click Submit.

    17. Repeat steps 8-10 to add the following field:

      Field Name
      Value
    18. Enable Conditions in the Enter your email address field.

    19. Click Add Condition.

    20. Select How should we contact you? from the dropwdown.

    21. Select email in the value field.

    22. Click Submit.

    23. Repeat steps 8-10 to add the following field:

      Field Name
      Value
    24. Select the Reorder option.

    25. Drag the Data consent group below the Information group.

    26. Click I am done reordering.

    27. Click Save.

    hashtag
    Configuring the Form Workflow

    Workflows is how you determine what you happen after your form is submitted. It could be actions like sending an email or displaying a "Thank You" message.

    To configure the Form workflow, follow these steps:

    1. Select the Submit message/ Go to page options in the bottom of the Forms editor.

    2. Enter a customised message in the Message on Submit field.

    3. Click Submit.

    4. Click on Send template email to [email protected].

    5. Enter an email address in the Sender Email field.

    6. By default, the Example-Template.cshtml template will be selected in the Email template field.

    7. Enable Attach Uploaded Files.

    8. Click Submit.

    9. Click Save.

    hashtag
    Configuring the Form Settings

    In this step, you will find the information about accessing the Forms Settings and the validations available to customise your form.

    To configure the form settings, follow these steps:

    1. Navigate to the Settings tab in the Forms editor.

    2. Scroll to find the Validation section.

    3. Ensure that the Mark Mandatory fields option is checked under Mark fields.

    4. Click Save.

    circle-info

    There are multiple settings that be configured. These are all optional in relation to this tutorial.

    hashtag
    Step 4: Adding the Contact Form to the Content Node

    Now that you have created your Contact Form, you can add it in the Contact Us Content Node using the Form Picke Data Type.

    To add the Contact Form to the Content Node, follow these steps:

    1. Go to the Content section in the Umbraco Backoffice.

    2. Open the Contact Us Page.

    3. Select Choose in the Contact Form field.

    4. Select the Insert Form with Theme option.

    5. Select the Contact Us Form.

    6. Click Choose.

    7. Click Save or Save and Publish.

    hashtag
    Step 5: Additional configuration

    In the next couple of steps, we will add some additional configuration required in order for our form to work properly.

    hashtag
    Configuring the reCAPTCHA value

    You need to update the configuration to include a value in the appsettings.json file.

    To configure the reCAPTCHA value, see the reCAPTCHA configuration article.

    hashtag
    Configuring Simple Mail Transfer Protocol (SMTP)

    By adding the SMTP settings in the appsettings.json file, you can send out emails from your Umbraco installation. It is required in order for your form to be able to send emails on submission.

    To configure the SMTP settings, see the Global Settingsarrow-up-right article.

    hashtag
    Step 6: Rendering the Contact Form

    In this step, we will render the values of the Contact Us Document Type in the template.

    To render the Contact Form, follow these steps:

    1. Go to the Settings section in the Umbraco Backoffice.

    2. Open the Contact Us template in the Templates folder.

    3. Enter the following code to render the form:

    circle-info
    1. Select Insert.

    2. Click Value.

    3. Select Document Type from the Choose field dropdown.

    4. Select Contact Us.

    5. Click Choose.

    6. Select bodyText from the Contact Us dropdown.

    7. Click Submit.

    8. Click Save.

    circle-info

    For Umbraco Forms to work correctly, you need to include some client dependencies. For more information, see the Preparing Your Frontend article.

    hashtag
    The final result

    Finally, it is time to view the Contact Form on the frontend.

    To view the Contact Form on the Frontend, follow these steps:

    1. Go to the Content section in the Umbraco Backoffice.

    2. Open the Contact Us Page.

    3. Ensure that the page is published.

    4. Go to the Info tab.

    5. Click on the Published link in the Links section.

    You now have a full-fledged Contact Form ready to be used on your website.

    Creating a Multi-Page Form

    In this tutorial, you will learn how to create a multi-page form using Umbraco Forms. Multi-page forms are particularly useful when you need to collect detailed information from users in a structured and user-friendly way.

    Multi-page forms are ideal for use cases such as event registration, job applications, booking a meeting, and so on.

    hashtag
    Prerequisites

    • Pre-built Website including a Document Type with the Form Picker Data Type.

    hashtag
    Log in to the Umbraco Backoffice

    1. Log in to the Umbraco backoffice.

    2. Go to the Forms section.

    hashtag
    Create a New Form

    1. Click + next to the Forms folder.

    2. Select New Form.

    1. Enter a Name for the form. For example: Book a Meeting.

    2. Click Save.

    hashtag
    Set up the First Page of the Form

    Let us begin by adding some fields to the first page of the form. By default, the Data Consent field is already available, and we will call this group Data Consent.

    To create a new group:

    1. Click Add new group.

    2. Enter the Name of the group. For example: Personal Information.

    3. Click Add question.

    Similarly, you can also add other relevant fields such as last name or email based on your requirements.

    For this tutorial, the following fields are added with the respective specifications:

    Field Name
    Data Type
    Field Type

    If you wish to reorder your fields, click Reorder.

    hashtag
    Create the Second Page

    To create a multi-page form, you need to add more pages:

    1. Click Add new page to create the second page of your form.

    2. Enter a Name for this page. For example: Company Information.

    3. Click Add question.

    Similarly, you can also add other relevant fields based on your requirements.

    hashtag
    Add Conditional Logic

    Umbraco Forms allows you to customize the flow of your multi-page form. You can add conditional logic to control which questions appear based on user inputs.

    To add conditions, follow these steps:

    1. Click Add question.

    2. Select Single choice as the field type.

    3. Enter a Name for the field type. For example, Do you work with Umbraco.

    1. Mark the field as Mandatory.

    2. Click Submit.

    3. Click Save.

    1. Enable Conditions.

    2. Set the parameters for the condition as follows:

      • Show this field if all of the following match:

    1. Click Submit.

    2. Click Save.

    hashtag
    Create the Final Page

    1. Click Add new page to create the final page of your form.

    2. Enter a Name for this page. For example: Products.

    3. Click Add question.

    1. Click Submit.

    2. Click Save.

    hashtag
    Embed the Form on a Web Page

    Once you are satisfied with your multi-page form, it is time to embed it on your website.

    To display the form on the website, follow these steps:

    1. Go to the Content section.

    2. Click + next to the parent page of the website.

    3. Select the Document Type.

    1. Enter a Name for the page. For example, Book a Meeting!

    2. Select the Book a Meeting form using the Form Picker.

    3. Click Save and Publish.

    hashtag
    Customize Form Settings

    If you wish to customize the Form Settings, see the article.

    hashtag
    Rendering the Form on the Frontend

    For Umbraco Forms to work correctly, you need to include some client dependencies. For more information, see the article.

    To render the Form on the frontend, see the article.

    hashtag
    Testing the Form

    1. Go to the Info workspace view of the Book a Meeting! page.

    2. Click on the Published link in the Links section.

    3. Fill out the form to see how it functions.

    You have successfully created a multi-page form with conditional logic in Umbraco Forms. By using multi-page forms, you have made complex data entry much simpler and more user-friendly. This not only improves the experience for your users but also makes your forms more efficient and manageable.

    @using Umbraco.Forms.Web.Helpers;
    @await Component.InvokeAsync("RenderForm", new { formId = Guid.Parse("<form guid>"), FormTheme = "bootstrap3-horizontal", ExcludeScripts = "1" })
    Replace **<form guid>** with the ID of your form. You can find the ID in the Form's **Info** tab.
    Select Short answer as the field type.
  • Enter a Name for the field type. For example, First Name.

  • Select Text as the Field Type from the drop-down list.

  • Mark the field as Mandatory.

  • Click Submit.

  • Click Save.

  • Short answer

    text

    Phone number

    Short answer

    tel

    Email address

    Short answer

    email

    Select Short answer as the field type.
  • Enter a Name for the field type. For example, Company Name.

  • Provide a Default Value. For example, Enter the name of your company.

  • Click Submit.

  • Click Save.

  • Enter the Value and Caption in the Options field.

    For this tutorial, the following values are added:

    • Yes

    • No

    Repeat steps 1-4 to create a conditional question titled: If yes, how many years?
  • Enter the Value and Caption in the Options field.

    For this tutorial, the following values are added:

    • 1-5 years

    • 5-10 years

    • 10+ years

  • Question: Do you work with Umbraco?

  • Condition: is

  • Value: Yes

  • Select Multiple choice as the field type.
  • Enter a Name for the field type. For example, Select the products you are interested in.

  • Enter the Value and Caption in the Options field.

    For this tutorial, the following values are added:

    • Umbraco CMS

    • Umbraco Cloud

    • Umbraco Deploy

    • Umbraco Heartcore

    • Umbraco Forms

    • Umbraco Commerce

    • Umbraco Workflow

  • Submit the form to ensure it redirects to a Thank You page.
  • Go to the Forms section in the Backoffice.

  • Navigate to the Book a Meeting Form.

  • Click on the Entries tab and verify that the data is captured.

  • Surname

    Short answer

    text

    Age

    Short answer

    number

    Umbraco CMS Installationarrow-up-right
    Umbraco Forms Package
    Form Settings
    Preparing Your Frontend
    Rendering Forms
    New Form Creation
    Personal Information Questions on the First Page
    Default Value displayed
    Values in the Options Field
    Conditional Question Values in the Options Field
    Values for adding a Condition
    Company information Questions on the Second Page
    Multiple Values in the Option Field
    Products Selection Question on the Final Page
    Pick a Document Type
    New Page created in the Content Section
    Form Data in the Entries tab

    Country

    Title Box

    Subtitle

    subtitle

    Textarea

    Content

    Body Text

    bodyText

    Richtext Editor

    Short answer

    Field Type

    text

    Mandatory

    On

    Field Name
    Value

    Enter question

    How should we contact you?

    Choose answer type

    Single choice

    Prevalues Items

    phone, email

    Mandatory

    Field Name
    Value

    Enter question

    Enter your phone number

    Choose answer type

    Short answer

    Field Type

    tel

    Validation

    Field Type

    email

    Validation

    Validate as an email address

    Prevalues Items

    manager, developer, tester, writer, marketing specialist

    Field Name
    Value

    Enter question

    Attachments (if any)

    Choose answer type

    File upload

    Predefined allowed file types

    pdf, png, jpg, gif, txt

    Field Name
    Value

    Enter question

    Are you a Robot?

    Choose answer type

    reCAPTCHAv2

    Theme

    light

    Size

    Add questions

    Title Box

    Title

    title

    Form

    Contact Form

    contactForm

    Enter question

    Name

    Alias

    fullName

    Enter question

    Company Name

    Choose answer type

    Short answer

    Enter question

    Enter your email address

    Choose answer type

    Short answer

    Enter question

    What is your role?

    Choose answer type

    Dropdown

    Add Composition Properties
    Contact Us Document Type Properties
    Update Home Page Document Type Properties
    Enter values in Contact Us Content node
    Adding the Contact Us Form

    Textstring

    Form Picker

    Choose answer type

    Workflow Types

    This article will give you an overview of the Workflow Types available in Umbraco Forms.

    There are multiple built-in Workflow Types that can be used to extend the functionality of your form. Do you want to post the submitted form as XML, send the data as an email, or send a notification through another messaging system? These are a few of the options you can choose when working with Umbraco Forms.

    hashtag
    Video Tutorial

    hashtag

    Change Record State
    Change Record state

    Used to automatically Approve Record, Reject Record or Delete Record once it is submitted. Configure words that you want to match and select whether these words should trigger an approval or deletion of the record.

    hashtag
    Post as XML

    Post as XML

    Used to post the Form as an XML to a specified URL. The following configuration can be set:

    • Workflow Name

    • URL (required)

    • Method

    • XsltFile - used to transform the XML

    • Headers - map the needed files

    • User

    • Password

    hashtag
    Save as an XML file

    Save as XML

    Saves the result of the Form as an XML file by using XSLT. The following configuration can be set:

    • Workflow Name

    • Path (required) - where to save the XML file

    • File extension (required)

    • XsltFile - used to transform the XML

    The path needs to point to a folder, not a file name. The files are then stored locally, and relative paths are resolved to the content root.

    circle-info

    When storing the files within the wwwroot or App_Plugins folders, the files will be publicly available by default.

    hashtag
    Save as Umbraco Content Node

    Save as content node

    Saves a submitted Form as a new content node. You need to choose a Document type and match the fields in the Form with the properties on the selected Document Type.

    You can also choose to set a static value to fill in the properties:

    Save as content node

    In the example above, a Document Type called Blogpost is selected for creating the new Content node.

    The value from the Name field will be added as the Node Name property in the new Content node. The value from the Email field will be used as the Content property.

    The following configuration can be set:

    • Workflow Name

    • Publish - choose whether to publish the node on submission

    • Where to save - choose a section in the content tree where this new node should be added

    hashtag
    Send Email

    Send email

    Sends the result of the Form to the specified email address. The following configuration can be set:

    • Workflow Name

    • Message (required)

    • Attachment - specify whether file uploads should be attached to the email

    • Recipient Email (required)

    • CC Email

    • BCC Email

    • SenderEmail

    • Reply To Email

    • Subject of the email (required)

    For fields that accept multiple email addresses (Recipient Email, CC Email, BCC Email), you can separate addresses using semicolons (';') or commas (','). For example:

    If the Sender Email field is not populated, the address used will be read from CMS configuration.

    The Global Settingsarrow-up-right value configured at Umbraco:CMS:Global:Smtp:From will be used if provided.

    If that is not set, the Content Settingsarrow-up-right value configured at Umbraco:CMS:Content:Notifications:Email will be used.

    The fallback behavior also applies to the other email workflows.

    hashtag
    Send Email with Template (Razor)

    Send email with template

    Uses a template to send the results of the Form to a specified email address.

    You can create your own custom Razor templates to be used to send out emails upon Forms submission. Read more about how to create these templates in the Email Templates article.

    The following configuration can be set:

    • Workflow Name

    • Email Template (required) - specify which template you want to use

    • Header text - formatted text that will be rendered above the form entry details

    • Footer text - formatted text that will be rendered below the form entry details

    • Attachments - specify whether file uploads should be attached to the email

    • Recipient Email (required)

    • CC Email

    • BCC Email

    • SenderEmail

    • Reply To Email

    • Subject of the email (required)

    hashtag
    Send Form to URL

    Send to URL

    Sends the Form to a URL either as a HTTP POST or GET. The following configuration can be set:

    • Workflow Name

    • URL (required)

    • Method (required) - POST, GET, PUT or DELETE

    • Standard Fields - optionally include and map standard form information such as name and page URL

    • Fields - map the needed fields

    • User

    • Password

    When mapping fields, if any are selected, only those chosen will be sent in the request to the configured URL. If no fields are mapped, all will be sent.

    The receiving endpoint extracts form fields and values using GET for querystrings and POST for form collections.

    As an illustrative example, the following code can be used to write the posted form information to a text file:

    hashtag
    Send XSLT Transformed Email

    Send XSLT Email

    Sends the result of the Form to an email address with full control over the email contents by providing an xslt file. The following configuration can be set:

    • Workflow Name

    • XSLT File - specify which file should be used to transform the content

    • Recipient Email (required)

    • CC Email

    • BCC Email

    • SenderEmail

    • Reply To Email

    • Subject of the email (required)

    hashtag
    Slack

    Send to Slack

    Allows to post the Form data to a specific channel on Slack. The following configuration can be set:

    • Workflow Name

    • Webhook URL (required)

    On

    Validate as a number

    normal

    Mandatory

    On

    Adding A Field Type To Umbraco Forms

    This builds on the "adding a type to the provider model" chapter

    In this article, we will illustrate how to add a custom form field type using server-side and client-side components. We will use the example of rendering a "slider" field type that allows the user to select a number within a specific range of values.

    hashtag
    Server-side Field Type Definition

    Add a new class to the Visual Studio solution. Inherit from Umbraco.Forms.Core.FieldType and complete as follows:

    In the constructor or via overridden properties, we can specify details of the field type:

    • Id - should be set to a unique GUID.

    • Alias - an internal alias for the field, used for localized translation keys.

    • Name - the name of the field presented in the backoffice.

    hashtag
    Configuration Validation

    The GetConfigurationErrors method can be overridden to report when required configuration is missing. By default it returns an empty collection, meaning the field type is considered configured and available for use.

    When the method returns one or more error messages, the field type will be shown as unavailable in the backoffice form builder until the issues are resolved. This is useful when your field type depends on external API keys or other application configuration settings.

    You now need to register this new field as a dependency:

    hashtag
    Partial View

    We will start building the view for the default theme of the Form at Views\Partials\Forms\Themes\default\FieldTypes\FieldType.Slider.cshtml.

    The file name for the partial view should match the value set on the FieldTypeViewName property.

    This will be rendered when the default theme is used.

    The theme is distributed as part of a Razor Class Library, so the folder won't exist on disk. However, you can create it for your custom field type. If you would like to reference the partial views of the default theme, you can download them as mentioned in the article.

    hashtag
    Read-only partial view

    When rendering a multi-page form, editors have the option to display a summary page where the entries can be viewed before submitting.

    To support this, a read-only view of the field is necessary.

    For most fields, nothing is required here, as the default read-only display defined in the built-in ReadOnly.cshtml file suffices.

    However, if you want to provide a custom read-only display for your field, you can do so by creating a second partial view. This should be named with a .ReadOnly suffix. For this example, you would create FieldType.Slider.ReadOnly.cshtml.

    hashtag
    Field Settings

    Field settings will be managed in the backoffice by editors who will create forms using the custom field type. These settings can be added to the C# class as properties with a Setting attribute:

    The property Name names the setting in the backoffice with the Description providing the help text. Both of these can be translated, as discussed in the backoffice components section below.

    The View property indicates a property editor UI used for editing the setting value. You can use a built-in property editor UI, one from a package, or a custom one registered with your solution. The default value if not provided is Umb.PropertyEditorUi.TextBox, which will use the standard Umbraco text box property editor UI.

    SupportsPlaceholders is a flag indicating whether the setting can contain and controls whether they are parsed on rendering.

    HtmlEncodeReplacedPlaceholderValues takes effect only if SupportsPlaceholders is true. It controls whether the replaced placeholder values should be HTML encoded (as is necessary for rendering within content from a rich text editor).

    SupportsHtml is a flag indicating whether the setting can contain HTML content. When set to true it will be treated as HTML content when the value is read from the Forms delivery API.

    IsMandatory if set to true will provide client-side validation in the backoffice to ensure the value is completed.

    Default values for settings can be defined in code using one of two approaches.

    Using a property initializer:

    Using the DefaultValue attribute property:

    If both are provided, the DefaultValue attribute property takes precedence over the property initializer.

    These code-based defaults provide an alternative to . If a value is configured in appsettings.json, it takes precedence over any code-based default.

    When creating a field or other provider type, you might choose to inherit from an existing class. This could be if one of the types provided with Umbraco Forms almost meets your needs but you want to make some changes.

    All setting properties for the Forms provider types are marked as virtual, so you can override them and change the setting values:

    hashtag
    Umbraco Backoffice Components

    With Forms 14+, aspects of the presentation and functionality of the custom field are handled by client-side components, registered via manifests:

    • The preview, displayed on the form definition editor.

    • The property editor UI used for editing the the submitted values via the backoffice.

    • The property editor UI used for editing settings.

    To create custom backoffice components for Umbraco 14, it's recommended to use a front-end build setup using Vite, TypeScript, and Lit. For more information, see the article.

    The examples here are using the @umbraco-forms/backoffice package to get access to Forms-specific types and contexts. It is recommended to install this package as a development dependency in your project.

    circle-exclamation

    Ensure that you install the version of the Backoffice package that is compatible with your Umbraco Forms installation. You can find the appropriate version on the .

    This will add a package to your devDependencies containing the TypeScript definitions for Umbraco Forms.

    To display a name and description on a custom field, you need to register a JavaScript file as shown in the article.

    hashtag
    Field Preview

    The alias of the preview to use is defined on the field type via the PreviewView property.

    A preview for our slider, representing the selected setting values could look as follows:

    And it is registered via a manifest:

    hashtag
    Field Editor

    Umbraco Forms supports editing of the entries submitted by website visitors via the backoffice. The property editor interface to use for this is defined in the field type's EditView property.

    If not using a built-in property editor, you can create your own. The following example shows how the numerical entries could be edited using an input control.

    Again, it's registered via a manifest.

    hashtag
    Setting Value Editor

    Field type settings also use a property editor UI for editing the values in the backoffice. The one to use is defined via the View property on the Setting attribute.

    In our example we use a custom one, allowing the value for the background color to the field to be selected via an input control.

    And register it via a manifest:

    hashtag
    Setting Value Converter

    You may want to consider registering a settings value converter. This is another client-side component that is registered in a manifest. It converts between the setting value required for the editor and the value persisted with the form definition. A converter defines three methods:

    • getSettingValueForEditor - converts the persisted string value into one suitable for the editor

    • getSettingValueForPersistence - converts the editor value into the string needed for persistence

    • getSettingPropertyConfig

    The following code shows the structure for these converter elements.

    It's registered as follows. The propertyEditorUiAlias matches with the property editor UI that requires the conversions.

    hashtag
    Language Files

    Setting labels and descriptions can be translated via language files. If no client-side localization is provided, the values provided server-side in the Setting attribute's Name and Description properties will be used.

    The following example shows how this is created for the settings on our example field type:

    Each different type of extension for Forms uses a different root value:

    • Data sources - formProviderDataSources

    • Export types - formProviderExportTypes

    • Field types - formProviderFieldTypes

    The language files are registered with:

    hashtag
    Registering the Components

    Finally, you will need an entry point to your client-side components that will register the manifests with Umbraco's extension registry. For example:

    [email protected]; [email protected], [email protected]
        "Umbraco": {
             "CMS": {
                "Global": {
                    "Smtp": {
                        "From": "[email protected]"
                    }
                }
            }
        }
        "Umbraco": {
             "CMS": {
                "Content": {
                    "Notifications": {
                        "Email": "[email protected]"
                    }
                }
            }
        }
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.Logging;
    using System.IO;
    
    namespace RequestSaver.Controllers
    {
        [ApiController]
        [Route("[controller]")]
        public class SaveRequestController : ControllerBase
        {
            private const string _filePath = "c:\\temp\\request-save.txt";
    
            private readonly ILogger<SaveRequestController> _logger;
    
            public SaveRequestController(ILogger<SaveRequestController> logger)
            {
                _logger = logger;
            }
    
            [HttpPost]
            public string Save()
            {
                using (StreamWriter outputFile = new StreamWriter(_filePath))
                {
                    foreach (var key in Request.Form.Keys)
                    {
                        outputFile.WriteLine($"{key}: {(Request.Form[key])}");
                    }
                }
    
                return "Done";
            }
        }
    }

    Description - the description of the field presented in the backoffice.

  • Icon - the icon of the field presented in the backoffice form builder user interface.

  • DataType - specifies the type of data stored by the field. Options are String, LongString, Integer, DateTime or Bit (boolean).

  • SupportsMandatory - indicates whether mandatory validation can be used with the field (defaults to true).

  • MandatoryByDefault - indicates whether the field will be mandatory by default when added to a form (defaults to false).

  • SupportsRegex - indicates whether pattern-based validation using regular expressions can be used with the field (defaults to false).

  • SupportsPreValues - indicates whether prevalues are supported by the field (defaults to false).

  • RenderInputType- indicates how the field should be rendered within the theme as defined with the RenderInputType enum.

    • The default is Single for a single input field.

    • Multiple should be used for multiple input fields such as checkbox lists.

    • Custom is used for fields without visible input fields.

  • FieldTypeViewName - indicates the name of the partial view used to render the field on the website.

  • EditView - indicates the name of a property editor UI that is used for editing the field in the backoffice. If nothing is provided, the built-in label will be used and the field won't be editable.

  • PreviewView - indicates the name of a manifest registered client-side resource that is used for previewing the field in the backoffice. If nothing is provided, the name of the field type will be used as the preview.

  • IsConfigured - indicates whether the field type is configured for use. This is derived from the GetConfigurationErrors method — it returns true when no configuration errors are reported.

  • A settings converter, that handles configuring the property editor and translating between the editor and persisted values.
  • Translations for setting labels and descriptions.

  • - creates the configuration needed for the property editor

    Prevalue sources - formProviderPrevalueSources

  • Recordset actions - formRecordSetActions

  • Workflows - formProviderWorkflows

  • Themes
    "magic string" placeholders
    configuring default values via appsettings.json
    Extension with Vite, TypeScript, and Litarrow-up-right
    @umbraco-forms/backoffice npm pagearrow-up-right
    Localizationarrow-up-right
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Forms.Core.Attributes;
    using Umbraco.Forms.Core.Enums;
    using Umbraco.Forms.Core.Providers;
    
    namespace MyProject;
    
    public class SliderFieldType : Core.FieldType
    {
        public SliderFieldType()
        {
            Id = new Guid("6dff0075-598c-4345-89d7-e0db8684c819");
            Name = "Slider";
            Alias = "slider";
            Description = "Render a UUI Slider field.";
            Icon = "icon-autofill";
            DataType = FieldDataType.String;
            SortOrder = 10;
    
            FieldTypeViewName = "FieldType.Slider.cshtml";
            EditView = "My.PropertyEditorUi.InputNumber";
            PreviewView = "My.FieldPreview.Slider";
        }
    
        [Setting("Minimum", Description = "Minimum value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 10)]
        public virtual string? Min { get; set; } = "1";
    
        [Setting("Maximum", Description = "Maximum value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 20)]
        public virtual string? Max { get; set; } = "1";
    
        [Setting("Step", Description = "Step size", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 30)]
        public virtual string? Step { get; set; } = "1";
    
        [Setting("Default Value", Description = "Default value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 40)]
        public virtual string? DefaultValue { get; set; } = "1";
    
        [Setting("Hide step values", Description = "Hides the numbers representing the value of each steps. Dots will still be visible", View = "Umb.PropertyEditorUi.Toggle", DisplayOrder = 50)]
        public virtual string? HideStepValues { get; set; }
    
        [Setting("Background color", Description = "Background color for the input field", View = "My.PropertyEditorUi.InputColor", DisplayOrder = 60)]
        public virtual string? BgColor { get; set; } = "1";
    }
    public override IEnumerable<string> GetConfigurationErrors()
    {
        if (string.IsNullOrWhiteSpace(_myRequiredApiKey))
        {
            yield return "MyFieldType requires an API key to be configured in appsettings.json.";
        }
    }
    using Umbraco.Cms.Core.Composing;
    using Umbraco.Cms.Core.DependencyInjection;
    using Umbraco.Forms.Core.Providers;
    
    namespace MyProject;
    
    public class Startup : IComposer
    {
        public void Compose(IUmbracoBuilder builder)
        {
            builder.WithCollectionBuilder<FieldCollectionBuilder>()
                .Add<SliderFieldType>();
        }
    }
    @using Umbraco.Forms.Web
    @model Umbraco.Forms.Web.Models.FieldViewModel
    @{
        var min = Model.GetSettingValue<int>("Min", 1);
        var max = Model.GetSettingValue<int>("Max", 10);
        var step = Model.GetSettingValue<int>("Step", 1);
        var bgColor = Model.GetSettingValue<string>("BgColor", "#fff");
    }
    <div>This is a custom "slider" field type. We'll just use an input to mock this up.</div>
    <input name="@Model.Name"
        style="background-color: @bgColor"
        id="@Model.Id"
        class="text @Html.GetFormFieldClass(Model.FieldTypeName)"
        value="@Model.ValueAsHtmlString"
        type="number"
        min="@min"
        max="@max"
        step="@step" />
    [Setting("Minimum", Description = "Minimum value", View = "Umb.PropertyEditorUi.Integer", DisplayOrder = 10)]
    public virtual string? Min { get; set; } = "1";
    [Setting("Minimum")]
    public virtual string? Min { get; set; } = "1";
    [Setting("Minimum", DefaultValue = "1")]
    public virtual string? Min { get; set; }
    npm install -D @umbraco-forms/[email protected]
    import {
      css,
      customElement,
      html,
      property,
    } from "@umbraco-cms/backoffice/external/lit";
    import { FormsFieldPreviewBaseElement } from "@umbraco-forms/backoffice";
    
    const elementName = "my-field-preview-slider";
    
    @customElement(elementName)
    export class MyFieldPreviewSliderElement extends FormsFieldPreviewBaseElement {
      @property()
      settings = {};
    
      @property({ type: Array })
      prevalues = [];
    
      render() {
        return html`<div
          style="background-color:${this.getSettingValue("BgColor")}"
        >
          <uui-slider
            .min=${parseInt(this.getSettingValue("Min"))}
            .max=${parseInt(this.getSettingValue("Max"))}
            .step=${this.getSettingValue("Step")}
            .value=${this.getSettingValue("DefaultValue")}
            ?hide-step-values=${this.getSettingValue("HideStepValues") === "True"}
          ></uui-slider>
        </div>`;
      }
    
      static styles = css`
        div {
          padding: var(--uui-size-4);
        }
      `;
    }
    
    export default MyFieldPreviewSliderElement;
    
    declare global {
      interface HTMLElementTagNameMap {
        [elementName]: MyFieldPreviewSliderElement;
      }
    }
    import MyFieldPreviewSliderElement from './slider-preview.element.js';
    import { ManifestFormsFieldPreview } from '@umbraco-forms/backoffice';
    
    const sliderPreviewManifest: ManifestFormsFieldPreview = {
      type: "formsFieldPreview",
      alias: "My.FieldPreview.Slider",
      name: "Forms UUI Slider Field Preview",
      element: MyFieldPreviewSliderElement
    };
    
    export const manifests = [sliderPreviewManifest];
    import {
      html,
      customElement,
    } from "@umbraco-cms/backoffice/external/lit";
    import type { UmbPropertyEditorUiElement } from "@umbraco-cms/backoffice/property-editor";
    import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";
    import {
      UmbChangeEvent,
    } from "@umbraco-cms/backoffice/event";
    import { UmbFormControlMixin } from "@umbraco-cms/backoffice/validation";
    
    const elementName = "my-property-editor-ui-number";
    
    @customElement(elementName)
    export class MyPropertyEditorUINumberElement
      extends UmbFormControlMixin<string, typeof UmbLitElement, undefined>(UmbLitElement, undefined)
      implements UmbPropertyEditorUiElement
    {
      private onChange(e: Event) {
        const newValue = (e.target as HTMLInputElement).value;
        if (newValue === this.value) return;
        this.value = newValue;
        this.dispatchEvent(new UmbChangeEvent());
      }
    
      override render() {
        return html`<uui-input
          .value=${this.value ?? ""}
          type="number"
          @input=${this.onChange}
        ></uui-input>`;
      }
    }
    
    export default MyPropertyEditorUINumberElement;
    
    declare global {
      interface HTMLElementTagNameMap {
        [elementName]: MyPropertyEditorUINumberElement;
      }
    }
    const numberPropertyEditorManifest = {
        type: 'propertyEditorUi',
        alias: 'My.PropertyEditorUi.InputNumber',
        name: 'Number Input Property Editor UI',
        element: () => import('./property-editor-ui-number.element.js'),
        meta: {
            label: 'Number Input',
            icon: 'icon-autofill',
        },
    };
    export const manifests = [numberPropertyEditorManifest];
    import {
      html,
      customElement,
      type PropertyValueMap,
    } from "@umbraco-cms/backoffice/external/lit";
    import type { UmbPropertyEditorUiElement } from "@umbraco-cms/backoffice/property-editor";
    import { UmbLitElement } from "@umbraco-cms/backoffice/lit-element";
    import {
      UmbChangeEvent,
    } from "@umbraco-cms/backoffice/event";
    import { UmbFormControlMixin } from "@umbraco-cms/backoffice/validation";
    
    const elementName = "my-property-editor-ui-color";
    
    @customElement(elementName)
    export class MyPropertyEditorUIColorElement
      extends UmbFormControlMixin<string>(UmbLitElement, undefined)
      implements UmbPropertyEditorUiElement
    {
      protected firstUpdated(
        _changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>
      ): void {
        super.firstUpdated(_changedProperties);
        this.addFormControlElement(this.shadowRoot!.querySelector("input")!);
      }
    
      private onChange(e: Event) {
        const newValue = (e.target as HTMLInputElement).value;
        if (newValue === this.value) return;
        this.value = newValue;
        this.dispatchEvent(new UmbChangeEvent());
      }
    
      override render() {
        return html`<input
          .value=${this.value ?? ""}
          type="color"
          @input=${this.onChange}
        />`;
      }
    }
    
    export default MyPropertyEditorUIColorElement;
    
    declare global {
      interface HTMLElementTagNameMap {
        [elementName]: MyPropertyEditorUIColorElement;
      }
    }
    const colorPropertyEditorManifest = {
        type: 'propertyEditorUi',
        alias: 'My.PropertyEditorUi.InputColor',
        name: 'Color Input Property Editor UI',
        element: () => import('./property-editor-ui-color.element.js'),
        meta: {
            label: 'Color Input',
            icon: 'icon-autofill',
        },
    };
    
    export const manifests = [colorPropertyEditorManifest];
    import type { UmbPropertyValueData } from "@umbraco-cms/backoffice/property";
    import type { FormsSettingValueConverterApi, Setting } from "@umbraco-forms/backoffice";
    
    export class SliderSettingValueConverter
      implements FormsSettingValueConverterApi {
      async getSettingValueForEditor(setting: Setting, alias: string, value: string) {
        return Promise.resolve(value);
      }
    
      async getSettingValueForPersistence(setting: Setting, valueData: UmbPropertyValueData) {
        return Promise.resolve(valueData.value?.toString() || "");
      }
    
      async getSettingPropertyConfig(setting: Setting, alias: string, values: UmbPropertyValueData[]) {
        return Promise.resolve([]);
      }
    
      destroy(): void { }
    }
    import { SliderSettingValueConverter } from "./slider-setting-value-converter.api";
    import { ManifestFormsSettingValueConverterPreview } from "@umbraco-forms/backoffice";
    
    const sliderValueConverterManifest: ManifestFormsSettingValueConverterPreview = {
      type: "formsSettingValueConverter",
      alias: "My.SettingValueConverter.Slider",
      name: "Slider Value Converter",
      propertyEditorUiAlias: "My.PropertyEditorUi.Slider",
      api: SliderSettingValueConverter,
    };
    
    export const manifests = [sliderValueConverterManifest];
    import type { UmbLocalizationDictionary } from "@umbraco-cms/backoffice/localization-api";
    
    export default {
      formProviderFieldTypes: {
        sliderMinLabel: `Minimum`,
        sliderMinDescription: `Minimum value`,
        sliderMaxLabel: `Maximum`,
        sliderMaxDescription: `Maximum value`,
        sliderStepLabel: `Step`,
        sliderStepDescription: `Step size`,
        sliderDefaultValueLabel: `Default Value`,
        sliderDefaultValueDescription: `Default value shown when the slider is displayed`,
        sliderHideStepValuesLabel: `Hide step values`,
        sliderHideStepValuesDescription: `Indicate whether the the field's label should be shown when rendering the form`,
        sliderBgColorLabel: `Background color`,
        sliderBgColorDescription: `Background color for the field`,
      },
    }
    import type { ManifestLocalization } from '@umbraco-cms/backoffice/localization';
    
    const localizationManifests: Array<ManifestLocalization> = [
      {
        type: "localization",
        alias: "My.Localization.En_US",
        weight: -100,
        name: "English (US)",
        meta: {
          culture: "en-us",
        },
        js: () => import("./en-us.js"),
      },
    ];
    export const manifests = [...localizationManifests];
    import { manifests as propertyEditorManifests } from "./property-editor/manifests.js";
    import { manifests as fieldPreviewManifests } from "./field-preview/manifests.js";
    import { manifests as settingValueConverterManifests } from "./setting-value-converter/manifests.js";
    import { manifests as localizationManifests } from "./lang/manifests.js";
    
    const manifests = [
      ...propertyEditorManifests,
      ...fieldPreviewManifests,
      ...settingValueConverterManifests,
      ...localizationManifests
    ];
    
    export const onInit = async (host, extensionRegistry) => {
      extensionRegistry.registerMany(manifests);
    };

    Headless/AJAX Forms

    Umbraco Forms provides an API for client-side rendering and submission of forms. This will be useful when you want to handle forms in a headless style scenario.

    hashtag
    Enabling the API

    The Forms API is disabled by default. To enable it, set the Umbraco:Forms:Options:EnableFormsApi configuration key to true.

    For example:

    hashtag
    API Definition

    The API supports two endpoints, one for rendering a form and one for submitting it.

    As well as this documentation, the definition of the API can also be reviewed via the Swagger UI.

    This is available alongside the Umbraco 12 Content Delivery Api at: /umbraco/swagger/index.html. Select "Umbraco Forms API" from the "Select a definition" list to view the methods available.

    The Open API specification is available from: /umbraco/swagger/forms/swagger.json

    hashtag
    Requesting a Form Definition

    To request the definition of a form, the following request can be made:

    The GET request requires the Guid identifying the form.

    An optional contentId parameter can be provided, which can either be the integer or GUID identifier for the current page. If provided, the content item identified will be used for Forms features requiring information from the page the form is hosted on. This includes the parsing of .

    A culture parameter can also be provided, expected as an ISO code identifying a language used in the Umbraco installation (for example, en-US). This will be used to ensure the correct translation for dictionary keys is used. It will also retrieve page content from the appropriate language variant. If the parameter is not provided in the request, the default Umbraco language will be used.

    Finally, an additionalData parameter can be provided as a dictionary. This information will be made available when rendering the form allowing it to be used as a source for .

    If the requested form is not found, a 404 status code will be returned.

    A successful request will return a 200 status code. An example response is as follows. It will differ depending on the pages, fields and other settings available for the form.

    It's possible to define either a message displayed when a form is submitted, or a redirect to another website page.

    With the former, the output will be as above, and as shown in this extract:

    When a redirect is configured, details of the content ID and a route will be included, as follows:

    hashtag
    Submitting a Form Entry

    To submit a form entry, the following request can be made:

    The POST request requires the Guid identifying the form.

    It also requires a Content-Type header of application/json and accepts a body as per this example:

    The values collection consists of a set of name/value pairs, where the name is the alias of a form field. The value is the value of the submitted field, which can either be a string, or an array of strings. In this way we support fields that accept multiple values, such as checkbox lists.

    The contentId and culture parameters are optional. If provided they will be used to customize the response for the current page and language respectively.

    Similarly the additionalData dictionary is optional. This data is associated with the created record and made available within workflows.

    In the case of a validation error, a 422 "Unprocessable Entity" status code will be returned, along with a response similar to the following:

    A successful response will return a 202 "Accepted" status code.

    It will contain an object detailing the post-submission configured the form, for example:

    hashtag
    File Uploads

    The file upload field type is supported via the API for the rendering and submission of forms.

    When retrieving a form definition, some additional detail is provided for fields of this type to allow for the appropriate rendering of the form interface:

    When submitting a form, the value should be a JSON structure that provides a collection. Each item in the collection should contain the file name and the file contents as a base64 encoded data URL.

    hashtag
    Securing the API

    hashtag
    Antiforgery Protection

    When posting forms in the traditional way, via a full page post back, an anti-forgery token is generated and validated. This provides protection against Cross-Site Request Forgery (CSRF) attacks.

    The same protection is available for forms submitted via AJAX techniques.

    In order to generate the token and provide it in the form post, the following code can be applied to the .cshtml template:

    When posting the form, the header value generated can be provided, where it will be validated server-side before accepting the request.

    hashtag
    API Key

    The antiforgery token security approach is valid when building a client-side integration with API calls made from the browser.

    Providing the token isn't possible though in other headless situations such as server-to-server requests. In these situations, an alternative approach to securing the API is available.

    Firstly, with server-to-server integrations you will want to disable the antiforgery token protection.

    This is done by setting the Umbraco:Forms:Security:EnableAntiForgeryTokenForFormsApi configuration key to a value of false.

    You should then configure an API key Umbraco:Forms:Security:FormsApiKey. This can be any string value, but it should be complex enough to resist being guessed by a brute force attack.

    With this in place any request to the Forms API will be rejected unless the configured value is provided in an HTTP header named Api-Key.

    hashtag
    Rendering and Submitting forms with JavaScript

    For an illustrative example showing how a form can be rendered, validated and submitted using the API and vanilla JavaScript, .

    Examples demonstrating how to handle a file upload and use reCAPTCHA fields are included.

    hashtag
    Working with the CMS Content Delivery API

    The provides headless capabilities within Umbraco by allowing you to retrieve content in JSON format.

    When retrieving content that contains an Umbraco Forms form picker, the output by default will consist of the ID of the selected form:

    With for the property, the full details of the form will be available. The structure and content of the form representation will be exactly the same as that provided by the Forms API itself.

    hashtag
    Dynamic Form injection

    For dynamic Form injection on a page, such as in a modal dialog, there's a specific JavaScript event and API method. This allows reinitializing Umbraco Forms for the new content.

    "magic string" placeholders
    "magic string" replacements
    see this gistarrow-up-right
    Content Delivery APIarrow-up-right
    expanded outputarrow-up-right
    Swagger UI
      "Umbraco": {
        "Forms": {
          "Options": {
            "EnableFormsApi": true
          }
        }
      }
    GET /umbraco/forms/delivery/api/v1/definitions/{id}?contentId={contentId}&culture={culture}&additionalData[{key}]={value}&additionalData[key2]={value2}
    {
        "disableDefaultStylesheet": false,
        "fieldIndicationType": "MarkMandatoryFields",
        "hideFieldValidation": false,
        "id": "34ef4a19-efa7-40c1-b8b6-2fd7257f2ed3",
        "indicator": "*",
        "messageOnSubmit": "Thanks for submitting the form",
        "name": "Simple Comment Form",
        "nextLabel": "Next",
        "pages": [
            {
                "caption": "Your comment",
                "fieldsets": [
                    {
                        "caption": "",
                        "columns": [
                            {
                                "caption": "",
                                "width": 12,
                                "fields": [
                                    {
                                        "alias": "name",
                                        "caption": "Name",
                                        "condition": {
                                            "actionType": "Show",
                                            "logicType": "All",
                                            "rules": []
                                        },
                                        "helpText": "[#message] from [#pageName]",
                                        "id": "25185934-9a61-491c-9610-83dfe774662c",
                                        "pattern": "",
                                        "patternInvalidErrorMessage": "Please provide a valid value for Name",
                                        "placeholder": "",
                                        "preValues": [],
                                        "required": true,
                                        "requiredErrorMessage": "Please provide a value for Name",
                                        "settings": {
                                            "defaultValue": "",
                                            "placeholder": "Please enter your name.",
                                            "showLabel": "",
                                            "maximumLength": "",
                                            "fieldType": "",
                                            "autocompleteAttribute": ""
                                        },
                                        "type": {
                                            "id": "3f92e01b-29e2-4a30-bf33-9df5580ed52c",
                                            "name": "Short answer"
                                        }
                                    },
                                    {
                                        "alias": "email",
                                        "caption": "Email",
                                        "condition": {
                                            "actionType": "Show",
                                            "logicType": "All",
                                            "rules": []
                                        },
                                        "helpText": "",
                                        "id": "816fdf3b-a796-4677-a317-943a54bf9d55",
                                        "pattern": "^[_a-z0-9-]+(\\.[_a-z0-9-]+)*@[a-z0-9-]+(\\.[a-z0-9-]+)*(\\.[a-z]{2,4})$",
                                        "patternInvalidErrorMessage": "Please provide a valid value for Email",
                                        "placeholder": "",
                                        "preValues": [],
                                        "required": true,
                                        "requiredErrorMessage": "Please provide a value for Email",
                                        "settings": {
                                            "defaultValue": "",
                                            "placeholder": "",
                                            "showLabel": "",
                                            "maximumLength": "",
                                            "fieldType": "email",
                                            "autocompleteAttribute": ""
                                        },
                                        "type": {
                                            "id": "3f92e01b-29e2-4a30-bf33-9df5580ed52c",
                                            "name": "Short answer"
                                        }
                                    },
                                    {
                                        "alias": "comment",
                                        "caption": "Comment",
                                        "condition": {
                                            "actionType": "Show",
                                            "logicType": "All",
                                            "rules": []
                                        },
                                        "helpText": "",
                                        "id": "9d723100-ec34-412f-aaa5-516634d7c833",
                                        "pattern": "",
                                        "patternInvalidErrorMessage": "Please provide a valid value for Comment",
                                        "placeholder": "",
                                        "preValues": [],
                                        "required": false,
                                        "requiredErrorMessage": "Please provide a value for Comment",
                                        "settings": {
                                            "defaultValue": "",
                                            "placeholder": "",
                                            "showLabel": "",
                                            "autocompleteAttribute": "",
                                            "numberOfRows": "2",
                                            "maximumLength": ""
                                        },
                                        "type": {
                                            "id": "023f09ac-1445-4bcb-b8fa-ab49f33bd046",
                                            "name": "Long answer"
                                        }
                                    },
                                    {
                                        "alias": "country",
                                        "caption": "Country",
                                        "condition": {
                                            "actionType": "Show",
                                            "logicType": "All",
                                            "rules": []
                                        },
                                        "helpText": "",
                                        "id": "30ff8f37-28d4-47df-f281-422b36c62e73",
                                        "pattern": "",
                                        "patternInvalidErrorMessage": "Please provide a valid value for Country",
                                        "placeholder": "",
                                        "preValues": [
                                            {
                                                "caption": "France",
                                                "value": "fr"
                                            },
                                            {
                                                "caption": "Italy",
                                                "value": "it"
                                            },
                                            {
                                                "caption": "Span",
                                                "value": "es"
                                            },
                                            {
                                                "caption": "United Kingdom",
                                                "value": "gb"
                                            }
                                        ],
                                        "required": false,
                                        "requiredErrorMessage": "Please provide a value for Country",
                                        "settings": {
                                            "defaultValue": "",
                                            "allowMultipleSelections": "",
                                            "showLabel": "",
                                            "autocompleteAttribute": "",
                                            "selectPrompt": "Please select"
                                        },
                                        "type": {
                                            "id": "0dd29d42-a6a5-11de-a2f2-222256d89593",
                                            "name": "Dropdown"
                                        }
                                    },
                                    {
                                        "alias": "favouriteColour",
                                        "caption": "Favourite Colour",
                                        "condition": {
                                            "actionType": "Show",
                                            "logicType": "All",
                                            "rules": []
                                        },
                                        "helpText": "",
                                        "id": "a6e2e27f-097d-476a-edb9-4aa79449ab5c",
                                        "pattern": "",
                                        "patternInvalidErrorMessage": "Please provide a valid value for Favourite Colour",
                                        "placeholder": "",
                                        "preValues": [
                                            {
                                                "caption": "Red",
                                                "value": "red"
                                            },
                                            {
                                                "caption": "Green",
                                                "value": "green"
                                            },
                                            {
                                                "caption": "Yellow",
                                                "value": "yello"
                                            }
                                        ],
                                        "required": false,
                                        "requiredErrorMessage": "Please provide a value for Favourite Colour",
                                        "settings": {
                                            "defaultValue": "",
                                            "showLabel": ""
                                        },
                                        "type": {
                                            "id": "fab43f20-a6bf-11de-a28f-9b5755d89593",
                                            "name": "Multiple choice"
                                        }
                                    },
                                    {
                                        "alias": "dataConsent",
                                        "caption": "Data consent",
                                        "condition": {
                                            "actionType": "Show",
                                            "logicType": "All",
                                            "rules": []
                                        },
                                        "helpText": "Please indicate if it's OK to store your data.",
                                        "id": "9f25acaf-4ac4-4105-9afe-eb0bb0c03b31",
                                        "pattern": "",
                                        "patternInvalidErrorMessage": "Please provide a valid value for Data consent",
                                        "placeholder": "",
                                        "preValues": [],
                                        "required": true,
                                        "requiredErrorMessage": "Please confirm your data consent",
                                        "settings": {
                                            "acceptCopy": "Yes, I give permission to store and process my data.",
                                            "showLabel": ""
                                        },
                                        "type": {
                                            "id": "a72c9df9-3847-47cf-afb8-b86773fd12cd",
                                            "name": "Data Consent"
                                        }
                                    },
                                    {
                                        "alias": "tickToAddMoreInfo",
                                        "caption": "Tick to add more info",
                                        "condition": {
                                            "actionType": "Show",
                                            "logicType": "All",
                                            "rules": []
                                        },
                                        "helpText": "",
                                        "id": "6ce0cf78-5102-47c1-85c6-9530d9e9c6a6",
                                        "pattern": "",
                                        "patternInvalidErrorMessage": "Please provide a valid value for Tick to add more info",
                                        "placeholder": "",
                                        "preValues": [],
                                        "required": false,
                                        "requiredErrorMessage": "Please provide a value for Tick to add more info",
                                        "settings": {
                                            "defaultValue": ""
                                        },
                                        "type": {
                                            "id": "d5c0c390-ae9a-11de-a69e-666455d89593",
                                            "name": "Checkbox"
                                        }
                                    },
                                    {
                                        "alias": "moreInfo",
                                        "caption": "More info",
                                        "condition": {
                                            "actionType": "Show",
                                            "logicType": "All",
                                            "rules": [
                                                {
                                                    "field": "6ce0cf78-5102-47c1-85c6-9530d9e9c6a6",
                                                    "operator": "Is",
                                                    "value": "on"
                                                }
                                            ]
                                        },
                                        "helpText": "",
                                        "id": "5b4100ed-cc5e-4113-943c-ee5a8f4e448d",
                                        "pattern": "",
                                        "patternInvalidErrorMessage": "Please provide a valid value for More info",
                                        "placeholder": "",
                                        "preValues": [],
                                        "required": false,
                                        "requiredErrorMessage": "Please provide a value for More info",
                                        "settings": {
                                            "defaultValue": "",
                                            "placeholder": "",
                                            "showLabel": "",
                                            "maximumLength": "",
                                            "fieldType": "",
                                            "autocompleteAttribute": ""
                                        },
                                        "type": {
                                            "id": "3f92e01b-29e2-4a30-bf33-9df5580ed52c",
                                            "name": "Short answer"
                                        }
                                    }
                                ],
                                "width": 0
                            }
                        ],
                        "id": "d677b96f-488d-4052-b00d-fb852b35e9c5"
                    }
                ]
            }
        ],
        "previousLabel": "Previous",
        "showValidationSummary": false,
        "submitLabel": "Submit"
    }
    {
        ...
        "messageOnSubmit": "Thanks for submitting the form",
        ...
    }
    {
        ...
        "gotoPageOnSubmit": "eab72f13-b22e-46d5-b270-9c196e49a53b",
        "gotoPageOnSubmitRoute": {
            "path": "/contact-us/thanks/",
            "startItem": {
                "id": "ca4249ed-2b23-4337-b522-63cabe5587d1",
                "path": "home"
            }
        },
        ...
    }
    POST /umbraco/forms/delivery/api/v1/entries/{id}
    {
        "values": {
            "name": "Fred",
            "email": "[email protected]",
            "comment": "Test",
            "country": "it",
            "favouriteColours": ["red", "green"],
            "dataConsent": "on"
        },
        "contentId": "ca4249ed-2b23-4337-b522-63cabe5587d1",
        "culture": "en-US",
        "additionalData": {
            "foo": "bar",
            "baz": "buzz",
        }
    }
    {
        "errors": {
            "name": [
                "Please provide a value for Name"
            ]
        },
        "extensions": {},
        "status": 422,
        "title": "One or more validation errors occurred."
    }
    {
        "gotoPageOnSubmit": "3cce2545-e3ac-44ec-bf55-a52cc5965db3",
        "gotoPageOnSubmitRoute": {
            "path": "/about-us/",
            "startItem": {
                "id": "ca4249ed-2b23-4337-b522-63cabe5587d1",
                "path": "home"
            }
        },
        "messageOnSubmit": "Thanks for your entry",
        "messageOnSubmitIsHtml": false
    }
        ...
        "fields": [
            {
                "alias": "uploadAPicture",
                ...
                "fileUploadOptions": {
                    "allowAllUploadExtensions": false,
                    "allowedUploadExtensions": [
                        "png",
                        "jpg",
                        "gif"
                    ],
                    "allowMultipleFileUploads": false
                },
            }
        ]
        ...
    {
        "values": {
            "uploadAPicture": [
                {
                    "fileName": "mypic.jpg",
                    "fileContents": "data:image/png;base64,iVBORw..."
                }
            ]
        },
    }
    @using Microsoft.AspNetCore.Antiforgery
    
    @inject IAntiforgery antiforgery
    
    @{
        var tokenSet = antiforgery.GetAndStoreTokens(Context);
    }
        let response = await fetch("/umbraco/forms/delivery/api/v1/entries/" + formId, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "@tokenSet.HeaderName" : "@tokenSet.RequestToken"
            },
            body: JSON.stringify(data),
        });
    {
        "name": "Contact Us",
        "route": {
            "path": "/contact-us/",
            "startItem": {
                "id": "ca4249ed-2b23-4337-b522-63cabe5587d1",
                "path": "home"
            }
        },
        "id": "4a1f4198-e143-48ba-a0f5-1a7ef2df23aa",
        "contentType": "contactPage",
        "properties": {
            "title": "Contact Us",
            "contactForm": {
                "id": "3623b232-9296-4bf0-b16c-57801dc4f296",
                "form": null
            }
        }
    }
    {
        "name": "Contact Us",
        "route": {
            "path": "/contact-us/",
            "startItem": {
                "id": "ca4249ed-2b23-4337-b522-63cabe5587d1",
                "path": "home"
            }
        },
        "id": "4a1f4198-e143-48ba-a0f5-1a7ef2df23aa",
        "contentType": "contactPage",
        "properties": {
            "title": "Contact Us",
            "contactForm": {
                "id": "3623b232-9296-4bf0-b16c-57801dc4f296",
                "form": {
                    "id": "3623b232-9296-4bf0-b16c-57801dc4f296",
                    "name": "Contact Form",
                    ...
                    "pages": [ ... ]
                }
            }
        }
    }
    // Execute a reinitialize on dynamic injections
    const reinitializeEvent = new Event('umbracoFormsReinitialize');
    document.dispatchEvent(reinitializeEvent);
    
    // Render a specific form via the API
    const injectedForm = document.getElementById('injected-umbraco-form');
    window.UmbracoForms.reinitialize(injectedForm);

    Configuration

    In Umbraco Forms it's possible to customize the functionality with various configuration values.

    With Umbraco Forms it's possible to customize the functionality with various configuration values.

    hashtag
    Editing configuration values

    All configuration for Umbraco Forms is held in the appsettings.json file found at the root of your Umbraco website. If the configuration has been customized to use another source, then the same keys and values discussed in this article can be applied there.

    The convention for Umbraco configuration is to have package based options stored as a child structure below the Umbraco element, and as a sibling of CMS. Forms configuration follows this pattern, i.e.:

    All configuration for Forms is optional. In other words, all values have defaults that will be applied if no configuration is available for a particular key.

    For illustration purposes, the following structure represents the full set of options for configuration of Forms, along with the default values. This will help when you need to provide a different setting to understand where it should be applied.

    hashtag
    Form design configuration

    hashtag
    DisableAutomaticAdditionOfDataConsentField

    This configuration value expects a true or false value and can be used to disable the feature where all new forms are provided with a default "Consent for storing submitted data" field on creation. Defaults to false.

    hashtag
    DisableDefaultWorkflow

    This configuration value expects a true or false value and can be used to toggle if new forms that are created adds an email workflow to send the result of the form to the current user who created the form. Defaults to false.

    hashtag
    MaxNumberOfColumnsInFormGroup

    This setting controls the maximum number of columns that can be created by editors when they configure groups within a form. The default value used if the setting value is not provided is 12.

    hashtag
    DefaultTheme

    This setting allows you to configure the name of the theme to use when an editor has not specifically selected one for a form. If empty or missing, the default value of "default" is used. If a custom default theme is configured, it will be used for rendering forms where the requested file exists, and where not, will fall back to the out of the box default theme.

    hashtag
    DefaultEmailTemplate

    When creating an empty form, a single workflow is added that will send an email to the current user's address. By default, the template shipped with Umbraco Forms is available at Forms/Emails/Example-Template.cshtml is used.

    If you have created a custom template and would like to use that as the default instead, you can set the path here using this configuration setting.

    hashtag
    RemoveProvidedFormTemplates

    Similarly, the provided form templates available from the form creation dialog can be removed from selection. To do this, set this configuration value to true.

    hashtag
    FormElementHtmlIdPrefix

    By default the value of HTML id attribute rendered for fieldsets and fields using the default theme is the GUID associated with the form element. Although , some browsers, particularly Safari, may report issues with this if the identifier begins with a number. To avoid such issues, the attribute values can be prefixed with the value provided in this configuration element.

    For example, providing a value of "f_" will apply a prefix of "f_" to each fieldset and field id attribute.

    hashtag
    SettingsCustomization

    Forms allows you to configure default values and visibility for field, workflow, data source, and prevalue source settings. Default values can be set in two ways:

    1. In code - by using the DefaultValue property on the , or a property initializer, when defining custom or extended provider types.

    2. In configuration - by using the SettingsCustomization section, which takes precedence over code-based defaults.

    Without any configuration, the default behavior when a new field or workflow is added to a form is for each setting to be empty. The values are then completed by the editor. All settings defined on the type are displayed for entry.

    In some situations, you may want to hide certain settings from entry, so they always take an empty value. In others, you may want to provide a default value that the editor can accept or amend. And lastly, you may have a requirement for a fixed, non-empty value, that's enforced by the organization and not editable. Each of these scenarios can be supported by this configuration setting.

    It consists of four dictionaries, one for each type:

    • DataSourceTypes

    • FieldTypes

    • PrevalueSourceTypes

    Each dictionary can be identified using the GUID or alias of the type as the key. The value is set to the following structure that contains three settings:

    • IsHidden - if provided and set to true the setting will be hidden and will always have an empty value.

    • DefaultValue - if provided the value will be pre-filled when a type using it is created.

    • IsReadOnly

    In this example, the sender address field on a workflow for sending emails can be hidden, such that the system configured value is always used:

    Here an organization-approved reCAPTCHA score threshold is defined, that can't be changed by editors:

    In order to configure this setting, you will need to know the GUID or alias for the type and the property name for each setting. You can find e.

    Take care to not hide any settings that are required for the particular field or workflow type (for example, the Subject field for email workflows). If you do that, the item will fail validation when an editor tries to create it.

    The default value and read-only settings apply to most setting types. There is an exception for complex ones where a default string value isn't appropriate. An example of one of these is the field mapper used in the "Send to URL" workflow.

    hashtag
    MandatoryFieldsetLegends

    When creating a form with Umbraco Forms, adding captions to the groups for fields is optional. To follow accessibility best practices, these fields should be completed. When they are, the group of fields are presented within a <fieldset> element that has a populated <legend>.

    If you want to ensure form creators always have to provide a caption, you can set the value of this setting to true.

    hashtag
    UseViewEngineFormThemeResolver

    Switches the IFormThemeResolver to use the View Engine (ICompositeViewEngine) to resolve theme views. This is done instead of relying on physical files to exist and doing I/O lookups via the PartialViews file system abstraction. To take advantage of this new resolver (available since 16.1), you can set the value of this setting to true.

    hashtag
    Form default settings configuration

    The following configured values are applied to all forms as they are created. They can then be amended on a per-form basis via the Umbraco backoffice.

    Once the form has been created, the values are set explicitly on the form, so subsequent changes to the defaults in configuration won't change the settings used on existing forms.

    hashtag
    ManualApproval

    This setting needs to be a true or false value and will allow you to toggle if a form allows submissions to be post moderated. Most use cases are for publicly shown entries such as blog post comments or submissions for a social campaign. Defaults to false.

    hashtag
    DisableStylesheet

    This setting needs to be a true or false value and will allow you to toggle if the form will include some default styling with the Umbraco Forms CSS stylesheet. Defaults to false.

    hashtag
    MarkFieldsIndicator

    This setting can have the following values to allow you to toggle the mode of marking mandatory or optional fields:

    • NoIndicator (default)

    • MarkMandatoryFields

    • MarkOptionalFields

    hashtag
    Indicator

    This setting is used to mark the mandatory or optional fields based on the setting above. By default this is an asterisk *.

    hashtag
    RequiredErrorMessage

    This allows you to configure the required error validation message. By default this is Please provide a value for {0} where the {0} is used to replace the name of the field that is required.

    hashtag
    InvalidErrorMessage

    This allows you to configure the invalid error validation message. By default this is Please provide a valid value for {0} where the {0} is used to replace the name of the field that is invalid.

    hashtag
    ShowValidationSummary

    This setting needs to be a true or false value and will allow you to toggle if the form will display all form validation error messages in a validation summary together. Defaults to false.

    hashtag
    HideFieldValidationLabels

    This setting needs to be a true or false value and will allow you to toggle if the form will show inline validation error messages next to the form field that is invalid. Defaults to false.

    hashtag
    NextPageButtonLabel, PreviousPageButtonLabel, SubmitButtonLabel

    These settings configure the default next, previous, and submit button labels. By default, these are Next, Previous, and Submit respectively. These labels can be amended on a form-by-form basis via the form's Settings section.

    hashtag
    MessageOnSubmit

    This allows you to configure what text is displayed when a form is submitted and is not being redirected to a different content node. Defaults to Thank you.

    hashtag
    StoreRecordsLocally

    This setting needs to be a True or False value and will allow you to toggle if form submission data should be stored in the Umbraco Forms database tables. By default this is set to True.

    hashtag
    AutocompleteAttribute

    This setting provides a value to be used for the autocomplete attribute for newly created forms. By default the value is empty, but can be set to on or off to have that value applied as the attribute value used when rendering the form.

    hashtag
    DaysToRetainSubmittedRecordsFor

    Introduced in 10.2, this setting controls the initial value of the number of days to retain form submission records for newly created forms. By default the value is 0, which means records will not be deleted at any time and are retained forever.

    If set to a positive number, a date value calculated by taking away the number of days configured from the current date is found. Records in the 'submitted' state, that are older than this date, will be flagged for removal.

    hashtag
    DaysToRetainApprovedRecordsFor

    Applies as per DaysToRetainSubmittedRecordsFor but for records in the 'approved' state.

    hashtag
    DaysToRetainRejectedRecordsFor

    Applies as per DaysToRetainSubmittedRecordsFor but for records in the 'rejected' state.

    hashtag
    ShowPagingOnMultiPageForms

    Defines whether and where paging details are displayed for multi-page forms.

    hashtag
    PagingDetailsFormat

    Defines the paging details format for multi-page forms.

    hashtag
    PageCaptionFormat

    Defines the page caption format for multi-page forms.

    hashtag
    ShowSummaryPageOnMultiPageForms

    Defines whether summary pages are on by default for multi-page forms.

    hashtag
    SummaryLabel

    Defines the default summary label for multi-page forms.

    hashtag
    Package options configuration

    hashtag
    IgnoreWorkFlowsOnEdit

    This configuration expects a True or False string value, or a comma-separated list of form names, and allows you to toggle if a form submission is edited again, that the workflows on the form will re-fire after an update to the form submission. This is used in conjunction with the AllowEditableFormSubmissions configuration value. Defaults to True.

    hashtag
    AllowEditableFormSubmissions

    This configuration value expects a true or false value and can be used to toggle the functionality to allow a form submission to be editable and re-submitted. When the value is set to true it allows Form Submissions to be edited using the following querystring for the page containing the form on the site. ?recordId=GUID Replace GUID with the GUID of the form submission. Defaults to false.

    circle-info

    There was a typo in this setting where it had been named as AllowEditableFormSubmissions. This is the name that needs to be used in configuration for Forms 9. In Forms 10 this was be corrected to the now documented value of AllowEditableFormSubmissions.

    circle-exclamation

    Enable this feature ONLY if you understand the security implications.

    hashtag
    AppendQueryStringOnRedirectAfterFormSubmission

    When redirecting following a form submission, a TempData value is set that is used to ensure the submission message is displayed rather than the form itself. In certain situations, such as hosting pages with forms in IFRAMEs from other websites, this value is not persisted between requests.

    By setting the following value to True, a querystring value of formSubmitted=<id of submitted form>, will be used to indicate a form submitted on the previous request.

    hashtag
    CultureToUseWhenParsingDatesForBackOffice

    This setting has been added to help resolve an issue with multi-lingual setups.

    When Umbraco Forms stores data for a record, it saves the values submitted for each field into a dedicated table for each type (string, date etc.). It also saves a second copy of the record in a JSON structure which is more suitable for fast look-up and display in the backoffice. Date values are serialized using the culture used by the front-end website when the form entry is stored.

    When displaying the data in the backoffice, the date value needs to be parsed back into an actual date object for formatting. And this can cause a problem if the backoffice user is using a different language, and hence culture setting, than that used when the value was stored.

    The culture used when storing the form entry is recorded, thus we can ensure the correct value is used when parsing the date. However, this doesn't help for historically stored records. To at least partially mitigate the problem, when you have editors using different languages to a single language presented on the website front-end, you can set this value to match the culture code used on the website. This ensures the date fields in the backoffice are correctly presented.

    Taking an example of a website globalization culture code setting of "en-US" (and a date format of m/d/y), but an editor uses "en-GB" (which formats dates as of d/m/y). By setting the value of this configuration key to "en-US", you can ensure that the culture when parsing dates for presentation in the backoffice will match that used when the value was stored.

    If no value is set, and no culture value was stored alongside the form entry, the culture based on the language associated with the current backoffice user will be used.

    hashtag
    TriggerConditionsCheckOn

    This configuration setting provides control over the client-side event used to trigger conditions. The change event is the default used if this setting is empty. It can also be set to a value of input. The main difference seen here relates to text fields, with the "input" event firing on each key press, and the "change" only when the field loses focus.

    hashtag
    ScheduledRecordDeletion

    Scheduled deletion of records older than a specified number of days. It uses a background task to run the cleanup operation, which can be customized with the following settings.

    hashtag
    Enabled

    By default this value is false and no data will be removed. Even if forms are configured to have submitted data cleaned up, no records will be deleted. A note will be displayed in the backoffice indicating this status.

    Set to true to enabled the background task.

    hashtag
    FirstRunTime

    This setting configures when the record deletion process will run for the first time. If the value is not configured, the process will run 3 minutes after the website starts. The value is specified as a string in crontab format. For example, a value of "0 4 * * *" schedules the operation to start at 04:00.

    hashtag
    Period

    Defines how often the record deletion process will run. The default value is 1.00:00:00 which is equivalent to once every 24 hours. Shorter or longer periods can be set using different datetime strings.

    hashtag
    DisableRecordIndexing

    Set this value to true to disable the default behavior of indexing the form submissions into the Examine index.

    If indexing has already occurred, you will still need to manually remove the files (found in App_Data\TEMP\ExamineIndexes\UmbracoFormsRecords). They will be recreated if indexing is subsequently re-enabled.

    hashtag
    EnableFormsApi

    Set this value to true to enable the Forms API supporting headless and AJAX forms.

    hashtag
    EnableRecordingOfIpWithFormSubmission

    By default, the user's IP address is not recorded when a form is submitted and stored in the UFRecords database table.

    To include this information in the saved data, set this value to true.

    If recording IPs and your site is behind a proxy, load balancer or CDN, we recommend using to ensure the correct value for the client IP is resolved.

    hashtag
    UseSemanticFieldsetRendering

    In Forms 12.1 amends were made to the default theme for Forms that improved accessibility. Specifically we provide the option to use alternative markup for rendering checkbox and radio button lists. These use the more semantically correct fieldset and legend elements, instead of the previously used div and label.

    Although this semantic markup is preferred, it could be a presentational breaking change for those styling the default theme. As such we have made this markup improvement optional. You can opt into using it by setting this configuration value to true.

    In Umbraco 13 this configuration option will be removed and the semantic rendering made the only option.

    hashtag
    DisableClientSideValidationDependencyCheck

    When a form is rendered on the front-end website, a check is run to ensure that client-side validation framework is available and registered.

    You can disable this check by setting the value of this configuration key to true.

    If you are rendering your forms dependency scripts using the async attribute, you will need to disable this check.

    hashtag
    DisableRelationTracking

    Forms will by default track relations between forms and the content pages they are used on. This allows editors to see where forms are being used in their Umbraco website.

    If you would like to disable this feature, you can set the value of this setting to true.

    hashtag
    TrackRenderedFormsStorageMethod

    Forms tracks the forms rendered on a page in order that the associated scripts can be placed in a different location within the HTML. Usually this is used to at the bottom of the page.

    By default, HttpContext.Items is used as the storage mechanism for this tracking.

    You can optionally revert to the legacy behavior of using TempData by changing this setting from the default of HttpContextItems to TempData.

    hashtag
    EnableMultiPageFormSettings

    This setting determines whether are available to editors.

    By default the value is true. To disable the feature, set the value to false.

    hashtag
    EnableAdvancedValidationRules

    This setting determines whether are available to editors.

    By default, the value is false. This is partly because the feature is only considered for "power users", comfortable with crafting rules using the required JSON syntax. And partly as validating the rules on the client requires an additional front-end dependency.

    To make the feature available to editors and include the dependency when using @Html.RenderUmbracoFormDependencies(Url), set the value to true.

    hashtag
    Analytics processing configuration

    A background process runs periodically to aggregate form submission data into summary tables. This provides fast-loading analytics views in the backoffice. The following settings control this process:

    hashtag
    Enabled

    Set to true (the default) to enable the background analytics data processing task. When disabled, no pre-aggregated data is created, but the analytics views in the backoffice are still available.

    hashtag
    FirstRunTime

    Configures when the analytics processing task will run for the first time. If not configured, the task will start shortly after the site starts. The value must be specified as a cron expression. For example, a value of "0 1 * * *" schedules the first run at 01:00.

    hashtag
    Period

    Defines how often the analytics data processing task will run. The default value is 1.00:00:00, which is equivalent to once every 24 hours.

    hashtag
    RetainProcessedDataForDays

    Defines the number of days to retain pre-aggregated analytics summary data. Summary data older than this value will be removed by the processing task. The default value is 1095 (approximately 3 years). Set to 0 to retain data indefinitely.

    hashtag
    Security configuration

    hashtag
    DisallowedFileUploadExtensions

    When using the File Upload field in a form, editors can choose which file extensions they want to accept. When an image is expected, they can for example specify that only .jpg or .png files are uploaded.

    There are certain file extensions that in almost all cases should never be allowed, which are held in this configuration value. This means that even if an editor has selected to allow all files, any files that match the extensions listed here will be blocked.

    By default, .NET related code files like .config and .aspx are included in this deny list. You can add or - if you are sure - remove values from this list to meet your needs.

    hashtag
    AllowedFileUploadExtensions

    For further control, an "allow list" of extension can be provided via this setting. If provided, only the extensions entered as a comma separated list here will be accepted in file uploads through forms.

    hashtag
    EnableAntiForgeryToken

    This setting needs to be a true or false value and will enable the ASP.NET Anti Forgery Token and we recommend that you enable this option. Defaults to true.

    In certain circumstances, including hosting pages with forms in IFRAMEs from other websites, this may need to be set to false.

    hashtag
    SavePlainTextPasswords

    This setting needs to be a true or false value and controls whether password fields provided in forms will be saved to the database. Defaults to false.

    hashtag
    DisableFileUploadAccessProtection

    Protection was added to uploaded files to prevent users from accessing them if they aren't logged into the backoffice and have permission to manage the form for which the file was submitted. As a policy of being "secure by default", the out of the box behavior is that this access protection is in place.

    If for any reason you need to revert to the previous behavior, or have other reasons where you want to permit unauthenticated users from accessing the files, you can turn off this protection by setting this configuration value to true.

    hashtag
    DefaultUserAccessToNewForms

    This setting was added to add control over access to new forms. The default behavior is for all users to be granted access to newly created forms. To amend that to deny access, the setting can be updated to a value of Deny. A value of Grant or configuration with the setting absent preserves the default behavior.

    hashtag
    ManageSecurityWithUserGroups

    Ability to administer access to Umbraco Forms using Umbraco's user groups. This can be used instead or in addition to the legacy administration which is at the level of the individual user. Set this option to true to enable the user group permission management functionality.

    hashtag
    GrantAccessToNewFormsForUserGroups

    This setting takes a comma-separated list of user group aliases which will be granted access automatically to newly created forms. This setting only takes effect when ManageSecurityWithUserGroups is set to true.

    There are two "special" values that can be applied within or instead of the comma-separated list.

    A value of all will give access to the form to all user groups.

    A value of form-creator will give access to all the user groups that the user who created the form is part of.

    hashtag
    FormsApiKey and EnableAntiForgeryTokenForFormsApi

    Available from Forms 10.2.1, the FormsApiKey configuration setting can be used to secure the Forms Headless API in server-to-server integrations. When set, API calls will be rejected unless the value of this setting is provided in an HTTP header.

    Setting the value of EnableAntiForgeryTokenForFormsApi to false will disable the anti-forgery protection for the Forms Headless/AJAX API. You need to do this for server-to-server integrations where it's not possible to provide a valid anti-forgery token in the request.

    For more information, see the article.

    hashtag
    Field type specific configuration

    hashtag
    Date picker field type configuration

    hashtag
    DatePickerYearRange

    This setting is used to configure the Date Picker form field range of years that is available in the date picker. By default this is a small range of 10 years.

    hashtag
    DatePickerFormat

    A custom date format can be provided in if you want to override the default.

    hashtag
    DatePickerFormatForValidation

    If a custom date format is provided it will be used on the client side. A matching string in should be provided, so that server-side validation will match the expected format of the entry.

    hashtag
    reCAPTCHA v2 field type configuration

    hashtag
    PublicKey & PrivateKey

    Both of these configuration values are needed in order to use the "Recaptcha2" field type implementing legacy ReCaptcha V2 from Google. You can obtain both of these values after signing up to create a ReCaptcha key here -

    Google has renamed these recently and the Site Key refers to RecaptchaPublicKey and Secret Key is to be used for RecaptchaPrivateKey

    hashtag
    reCAPTCHA v3 field type configuration

    hashtag
    SiteKey & PrivateKey

    Both of these configuration values are needed in order to use the "reCAPTCHA V3 with Score" field type implementing ReCaptcha V3 from Google.

    You can obtain both of these values after signing up to create a ReCaptcha key here: .

    hashtag
    Domain

    This setting defines the domain from which the client-side assets for using the reCAPTCHA service are requested.

    Valid options are Google (the default) or Recaptcha. You may want to use the latter for control of which domains are setting cookies on your site. .

    hashtag
    VerificationUrl

    By default, the server-side validation of the reCAPTCHA response is sent to Google's servers at https://www.google.com/recaptcha/api/siteverify.

    Some customers with a locked-down production environment cannot configure the firewall to allow these requests and instead use a proxy server. They can use this setting to configure the URL to their proxy server, which will relay the request to and response from Google.

    hashtag
    ShowFieldValidation

    The validation message returned from a failed reCAPTCHA 3 request will be displayed in the form level validation summary and alongside the field.

    To remove rendering at the field level, set this value to false.

    hashtag
    reCAPTCHA Enterprise field type configuration

    hashtag
    SiteKey, ApiKey & ProjectId

    These configuration values are needed in order to use the "reCAPTCHA Enterprise with Score" field type implementing ReCaptcha Enterprise from Google.

    You can obtain these values after signing up to create a reCAPTCHA Enterprise key here: .

    hashtag
    Domain

    This setting defines the domain from which the client-side assets for using the reCAPTCHA service are requested.

    Valid options are Google (the default) or Recaptcha. You may want to use the latter for control of which domains are setting cookies on your site. .

    hashtag
    VerificationUrl

    By default, the server-side validation of the reCAPTCHA response is sent to Google's servers at https://recaptchaenterprise.googleapis.com/v1/projects/{PROJECT_ID}/assessments.

    Some customers with a locked-down production environment cannot configure the firewall to allow these requests and instead use a proxy server. They can use this setting to configure the URL to their proxy server, which will relay the request to and response from Google.

    hashtag
    ShowFieldValidation

    The validation message returned from a failed reCAPTCHA Enterprise request will be displayed in the form level validation summary and alongside the field.

    To remove rendering at the field level, set this value to false.

    hashtag
    Rich text field type configuration

    hashtag
    DataTypeId

    Sets the Data Type Guid to use to obtain the configuration for the rich text field type. If the setting is absent, the value of the default rich text Data Type created by Umbraco on a new install is used.

    hashtag
    Title and description field type configuration

    hashtag
    AllowUnsafeHtmlRendering

    When using the "title and description" field type, if editors provide HTML in the "description" field it will be encoded when rendering on the website.

    If you understand the risks and want to allow HTML to be displayed, you can set this value to false.

    WorkflowTypes

    - used in conjunction with the above, if set the field won't be editable and hence whatever is set as the
    DefaultValue
    won't be able to be changed. If set to false (or omitted) the editor can change the value from the default.
    this is validarrow-up-right
    Setting attribute
    these values for the built-in Forms types her
    ASP.NET's forwarded headers middlewarearrow-up-right
    render the scripts
    multi-page form settings
    advanced form validation rules
    Headless/AJAX Forms
    momentjs formatarrow-up-right
    C# date formatarrow-up-right
    https://www.google.com/recaptcha/adminarrow-up-right
    https://www.google.com/recaptcha/adminarrow-up-right
    Read more at the reCAPTCHA documentationarrow-up-right
    https://www.google.com/recaptcha/arrow-up-right
    Read more at the reCAPTCHA documentationarrow-up-right
    {
      ...
      "Umbraco": {
        "CMS": {
            ...
        },
        "Forms": {
            ...
        }
      }
    }
      "Forms": {
        "FormDesign": {
          "DisableAutomaticAdditionOfDataConsentField": false,
          "DisableDefaultWorkflow": false,
          "MaxNumberOfColumnsInFormGroup": 12,
          "DefaultTheme": "default",
          "DefaultEmailTemplate": "Forms/Emails/Example-Template.cshtml",
          "Defaults": {
            "ManualApproval": false,
            "DisableStylesheet": false,
            "MarkFieldsIndicator": "NoIndicator",
            "Indicator": "*",
            "RequiredErrorMessage": "Please provide a value for {0}",
            "InvalidErrorMessage": "Please provide a valid value for {0}",
            "ShowValidationSummary": false,
            "HideFieldValidationLabels": false,
            "NextPageButtonLabel": "Next",
            "PreviousPageButtonLabel": "Previous",
            "SubmitButtonLabel": "Submit",
            "MessageOnSubmit": "Thank you",
            "StoreRecordsLocally": true,
            "AutocompleteAttribute": "",
            "DaysToRetainSubmittedRecordsFor": 0,
            "DaysToRetainApprovedRecordsFor": 0,
            "DaysToRetainRejectedRecordsFor": 0,
            "ShowPagingOnMultiPageForms": "None",
            "PagingDetailsFormat": "Page {0} of {1}",
            "PageCaptionFormat": "Page {0}",
            "ShowSummaryPageOnMultiPageForms": false,
            "SummaryLabel": "Summary of Entry"
          },
          "RemoveProvidedFormTemplates": false,
          "FormElementHtmlIdPrefix": "",
          "SettingsCustomization": {
            "DataSourceTypes": {},
            "FieldTypes": {},
            "PrevalueSourceTypes": {},
            "WorkflowTypes": {},
          },
          "MandatoryFieldsetLegends": false,
          "UseViewEngineFormThemeResolver": false
        },
        "Options": {
          "IgnoreWorkFlowsOnEdit": "True",
          "AllowEditableFormSubmissions": false,
          "AppendQueryStringOnRedirectAfterFormSubmission": false,
          "CultureToUseWhenParsingDatesForBackOffice": "",
          "TriggerConditionsCheckOn": "change",
          "ScheduledRecordDeletion": {
            "Enabled": false,
            "FirstRunTime": "",
            "Period": "1.00:00:00"
          },
          "DisableRecordIndexing": false,
          "EnableFormsApi": false,
          "EnableRecordingOfIpWithFormSubmission": false,
          "UseSemanticFieldsetRendering": false,
          "DisableClientSideValidationDependencyCheck": false,
          "DisableRelationTracking": false,
          "TrackRenderedFormsStorageMethod": "HttpContextItems",
          "EnableMultiPageFormSettings": true,
          "EnableAdvancedValidationRules": false,
          "AnalyticsProcessing": {
            "Enabled": true,
            "FirstRunTime": "",
            "Period": "1.00:00:00",
            "RetainProcessedDataForDays": 1095
          }
        },
        "Security": {
          "DisallowedFileUploadExtensions": "config,exe,dll,asp,aspx",
          "AllowedFileUploadExtensions": "",
          "EnableAntiForgeryToken": true,
          "SavePlainTextPasswords": false,
          "DisableFileUploadAccessProtection": false,
          "DefaultUserAccessToNewForms": "Grant",
          "ManageSecurityWithUserGroups": false,
          "GrantAccessToNewFormsForUserGroups": "admin,editor",
          "FormsApiKey": "",
          "EnableAntiForgeryTokenForFormsApi": true,
        },
        "FieldTypes": {
          "DatePicker": {
            "DatePickerYearRange": 10,
            "DatePickerFormat": "LL",
            "DatePickerFormatForValidation": ""
          },
          "Recaptcha2": {
            "PublicKey": "",
            "PrivateKey": ""
          },
          "Recaptcha3": {
            "SiteKey": "",
            "PrivateKey": "",
            "Domain": "Google",
            "VerificationUrl": "https://www.google.com/recaptcha/api/siteverify",
            "ShowFieldValidation": true
          },
          "RecaptchaEnterprise": {
            "SiteKey": "",
            "ApiKey": "",
            "ProjectId": "",
            "Domain": "Google",
            "VerificationUrl": "https://recaptchaenterprise.googleapis.com/v1/projects/{PROJECT_ID}/assessments",
            "ShowFieldValidation": true
          },
          "RichText": {
            "DataTypeId": "ca90c950-0aff-4e72-b976-a30b1ac57dad"
          },
          "TitleAndDescription": {
            "AllowUnsafeHtmlRendering": false
          }
        }
      }
    {
      "IsHidden": true|false,
      "DefaultValue": "",
      "IsReadOnly": true|false
    }
      "SettingsCustomization": {
        "WorkflowTypes": {
          "sendEmailWithRazorTemplate": {
            "SenderEmail": {
              "IsHidden": true
            }
          }
        },
      }
      "SettingsCustomization": {
        "FieldTypes": {
          "recaptcha3": {
            "ScoreThreshold": {
              "DefaultValue": "0.8",
              "IsReadOnly": true
            }
          }
        },
      }
    Creating a Contact Us Form using Umbraco Forms
    Workflow Types in Umbraco Forms
    get
    Path parameters
    idstring · uuidRequired

    The form's Id.

    Query parameters
    contentIdstringOptional

    The Id of the content page on which the form is hosted.

    culturestringOptional

    The culture code for the form's localization context.

    Other propertiesstringOptional
    Responses
    chevron-right
    200

    Success

    application/json
    idstring · uuidOptional
    namestringOptional
    indicatorstringOptional
    cssClassstring · nullableOptional
    nextLabelstring · nullableOptional
    previousLabelstring · nullableOptional
    submitLabelstring · nullableOptional
    disableDefaultStylesheetbooleanOptional
    fieldIndicationTypeinteger · enumOptionalPossible values:
    hideFieldValidationbooleanOptional
    messageOnSubmitstring · nullableOptional
    messageOnSubmitIsHtmlbooleanOptional
    showValidationSummarybooleanOptional
    gotoPageOnSubmitstring · uuid · nullableOptional
    pathstringRead-onlyOptional
    idstring · uuidRead-onlyOptional
    pathstringRead-onlyOptional
    captionstring · nullableOptional
    actionTypeinteger · enumOptionalPossible values:
    logicTypeinteger · enumOptionalPossible values:
    fieldstringOptional
    operatorinteger · enumOptionalPossible values:
    valuestringOptional
    idstring · uuidOptional
    captionstring · nullableOptional
    actionTypeinteger · enumOptionalPossible values:
    logicTypeinteger · enumOptionalPossible values:
    fieldstringOptional
    operatorinteger · enumOptionalPossible values:
    valuestringOptional
    captionstring · nullableOptional
    widthinteger · int32Optional
    idstring · uuidOptional
    captionstringOptional
    helpTextstring · nullableOptional
    placeholderstring · nullableOptional
    cssClassstring · nullableOptional
    aliasstringOptional
    requiredbooleanOptional
    requiredErrorMessagestring · nullableOptional
    patternstring · nullableOptional
    patternInvalidErrorMessagestring · nullableOptional
    actionTypeinteger · enumOptionalPossible values:
    logicTypeinteger · enumOptionalPossible values:
    fieldstringOptional
    operatorinteger · enumOptionalPossible values:
    valuestringOptional
    allowAllUploadExtensionsbooleanOptional
    allowedUploadExtensionsstring[]Optional
    allowMultipleFileUploadsbooleanOptional
    valuestringOptional
    captionstring · nullableOptional
    Other propertiesstringOptional
    idstring · uuidOptional
    namestringOptional
    supportsPreValuesbooleanOptional
    supportsUploadTypesbooleanOptional
    renderInputTypestringOptional
    chevron-right
    400

    Bad Request

    application/json
    typestring · nullableOptional
    titlestring · nullableOptional
    statusinteger · int32 · nullableOptional
    detailstring · nullableOptional
    instancestring · nullableOptional
    Other propertiesanyOptional
    chevron-right
    404

    Not Found

    application/json
    typestring · nullableOptional
    titlestring · nullableOptional
    statusinteger · int32 · nullableOptional
    detailstring · nullableOptional
    instancestring · nullableOptional
    Other propertiesanyOptional
    get
    /umbraco/forms/delivery/api/v1/definitions/{id}
    post
    Path parameters
    idstring · uuidRequired

    The form's Id.

    Body
    Other propertiesstring[]Optional
    contentIdstring · nullableOptional
    culturestring · nullableOptional
    Other propertiesstringOptional
    Responses
    chevron-right
    202

    Accepted

    chevron-right
    400

    Bad Request

    application/json
    typestring · nullableOptional
    titlestring · nullableOptional
    statusinteger · int32 · nullableOptional
    detailstring · nullableOptional
    instancestring · nullableOptional
    Other propertiesanyOptional
    chevron-right
    404

    Not Found

    application/json
    typestring · nullableOptional
    titlestring · nullableOptional
    statusinteger · int32 · nullableOptional
    detailstring · nullableOptional
    instancestring · nullableOptional
    Other propertiesanyOptional
    chevron-right
    422

    Client Error

    application/json
    post
    /umbraco/forms/delivery/api/v1/entries/{id}

    No content

    GET /umbraco/forms/delivery/api/v1/definitions/{id} HTTP/1.1
    Accept: */*
    
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "name": "text",
      "indicator": "text",
      "cssClass": "text",
      "nextLabel": "text",
      "previousLabel": "text",
      "submitLabel": "text",
      "disableDefaultStylesheet": true,
      "fieldIndicationType": "NoIndicator",
      "hideFieldValidation": true,
      "messageOnSubmit": "text",
      "messageOnSubmitIsHtml": true,
      "showValidationSummary": true,
      "gotoPageOnSubmit": "123e4567-e89b-12d3-a456-426614174000",
      "gotoPageOnSubmitRoute": {
        "path": "text",
        "startItem": {
          "id": "123e4567-e89b-12d3-a456-426614174000",
          "path": "text"
        }
      },
      "pages": [
        {
          "caption": "text",
          "condition": {
            "actionType": "Show",
            "logicType": "All",
            "rules": [
              {
                "field": "text",
                "operator": "Is",
                "value": "text"
              }
            ]
          },
          "fieldsets": [
            {
              "id": "123e4567-e89b-12d3-a456-426614174000",
              "caption": "text",
              "condition": {
                "actionType": "Show",
                "logicType": "All",
                "rules": [
                  {
                    "field": "text",
                    "operator": "Is",
                    "value": "text"
                  }
                ]
              },
              "columns": [
                {
                  "caption": "text",
                  "width": 1,
                  "fields": [
                    {
                      "id": "123e4567-e89b-12d3-a456-426614174000",
                      "caption": "text",
                      "helpText": "text",
                      "placeholder": "text",
                      "cssClass": "text",
                      "alias": "text",
                      "required": true,
                      "requiredErrorMessage": "text",
                      "pattern": "text",
                      "patternInvalidErrorMessage": "text",
                      "condition": {
                        "actionType": "Show",
                        "logicType": "All",
                        "rules": [
                          {
                            "field": "text",
                            "operator": "Is",
                            "value": "text"
                          }
                        ]
                      },
                      "fileUploadOptions": {
                        "allowAllUploadExtensions": true,
                        "allowedUploadExtensions": [
                          "text"
                        ],
                        "allowMultipleFileUploads": true
                      },
                      "preValues": [
                        {
                          "value": "text",
                          "caption": "text"
                        }
                      ],
                      "settings": {
                        "ANY_ADDITIONAL_PROPERTY": "text"
                      },
                      "type": {
                        "id": "123e4567-e89b-12d3-a456-426614174000",
                        "name": "text",
                        "supportsPreValues": true,
                        "supportsUploadTypes": true,
                        "renderInputType": "text"
                      }
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
    POST /umbraco/forms/delivery/api/v1/entries/{id} HTTP/1.1
    Content-Type: application/json
    Accept: */*
    Content-Length: 135
    
    {
      "values": {
        "ANY_ADDITIONAL_PROPERTY": [
          "text"
        ]
      },
      "contentId": "text",
      "culture": "text",
      "additionalData": {
        "ANY_ADDITIONAL_PROPERTY": "text"
      }
    }