EPiServer, Episerver Forms, ThisAndThat

Create custom Episerver Forms container

In this blog post I’ll show you how you can create a custom Episerver Forms form container block. Then I will extend it having custom properties to enter the Google Tag Manager related form submit event information.

This is another approach to my previous GTM blog post where I needed to be able to add the information to already existing forms – in that case I created new Episerver Forms block element to add the information.

Getting started

First thing is to install the Episerver Forms NuGet package from Episerver NuGet feed to your project. At the time of writing the latest Episerver Forms NuGet package version is 4.24.0 so I’ll be using that in this sample.

Episerver has instructions to create the custom form container block on their instructions page but the instructions currently make some assumptions and skip the information what you should copy as the base for the customized view file. Related to this is also the instruction to create a two column form container block.

So when you install the Episerver Forms package it will add its files to your project under the ‘modules/_protected‘ folder. The package adds two folders: ‘EPiServer.Forms‘ and ‘EPiServer.Forms.UI‘.

In the EPiServer.Forms.UI folder will be a zip package EPiServer.Forms.UI.zip which contains the JavaScript, CSS, etc. resource files needed by the ‘Episerver edit‘ view.

In the EPiServer.Forms folder will be a zip package ‘EPiServer.Forms.zip‘ which contains the embedded language files, resources for the view templates and more importantly the default Episerver Forms view templates (.ascx files) to render the container and form elements. This zip file contains the ‘FormContainerBlock’ view file that should be copied as the base for our custom view. The folder also contains the ‘Forms.config‘ file that can be used to configure Episerver Forms.

I use the Alloy MVC (the one you can create from the Episerver Visual Studio extension) site in the sample, so its default project structure is used.

Create block, controller and view for the custom form container

We need to implement a custom block to be the container for Forms element blocks. We also need to have a custom controller for our new container block and then a view that renders our container.

Container block implementation

Our custom Forms container block needs to inherit the Episerver base class ‘EPiServer.Forms.Implementation.Elements.FormContainerBlock’.

Create a new folder called ‘Forms‘ under the ‘Models‘ folder. Then add a new Episerver ‘Block Type‘ (using VS add new item) there called ‘SiteFormsContainerBlock‘. Add using statement ‘EPiServer.Forms.Implementation.Elements‘ to the code file and change your block to inherit the ‘FormContainerBlock‘ class. Next edit the ContentType attribute, add a description for your block (info to editors) and then also set this blocks group to be the same as the default container blocks group is (GroupName = EPiServer.Forms.Constants.FormElementGroup_Container).

We also need to tell Episerver about our custom container, so we need to add the ServiceConfiguration attribute to our class: [ServiceConfiguration(typeof(IFormContainerBlock))]. You need to add using statements for ‘EPiServer.Forms.Core’ and ‘EPiServer.ServiceLocation’.

Next we add two custom properties to the container: Google Tag Manager form name and category name. In this sample I will have just two string properties where the editor enters the values but for example for the category a list of possible values could be used (like I used in the previous post the GtmSelectionFactory).

using EPiServer.DataAbstraction;
using EPiServer.DataAnnotations;
using EPiServer.Forms.Core;
using EPiServer.Forms.Implementation.Elements;
using EPiServer.ServiceLocation;
using System.ComponentModel.DataAnnotations;
namespace SampleCustomFormContainer.Models.Forms
[ContentType(DisplayName = "SiteFormsContainerBlock",
GUID = "0d416689-3819-4812-b40d-2bdd62ef7eba",
Description = "Episerver Forms container with GTM submit event support.",
GroupName = EPiServer.Forms.Constants.FormElementGroup_Container)]
public class SiteFormsContainerBlock : FormContainerBlock
[Display(Name = "GTM form name", Description = "Enter the Google Tag Manager submit event form name.", GroupName = CustomGroupNames.GtmTabName, Order = 10)]
[CultureSpecific(true), Required]
public virtual string GtmFormName { get; set; }
[Display(Name = "GTM category name", Description = "Enter the Google Tag Manager submit event category name.", GroupName = CustomGroupNames.GtmTabName, Order = 20)]
[CultureSpecific(true), Required]
public virtual string GtmCategoryName { get; set; }
public static class CustomGroupNames
// for demo purposes to have this class here
// normally you have your sites tab definitions in one common place
[Display(Name = "Google Tag Manager", Order = 20)]
public const string GtmTabName = "custom-gtm";

Controller implementation

Next step is to implement the controller for our custom container block. Episerver has a base class for this, so our controller should inherit the ” class.

Add new controller under folder ‘Controllers’ using the Episerver  ‘Block Controller (MVC)’ template and name the new controller ‘SiteFormsContainerBlockController’. Add using statements: ‘EPiServer.Forms.Implementation.Elements‘ and ‘EPiServer.Forms.Controllers‘. Switch the new controller to inherit FormContainerBlockController class and change the type for the index method to ‘FormContainerBlock’ currentBlock. Change the Index method implementation to use ‘return base.Index(currentBlock);’

using EPiServer.Forms.Controllers;
using EPiServer.Forms.Implementation.Elements;
using System.Web.Mvc;
namespace SampleCustomFormContainer.Controllers
public class SiteFormsContainerBlockController : FormContainerBlockController
public override ActionResult Index(FormContainerBlock currentBlock)
return base.Index(currentBlock);

View implementation

So here comes the part where we implement our custom view for the container. First step is to get our “base” view implementation. This is the hacky part, we need to extract the Episerver implementation from the EPiServer.Forms.zip file and then copy paste it as our implementation.

I’ll use the default location for custom Forms view files which is defined in the ‘modules\_protected\EPiServer.Forms\Forms.config‘, see the ‘formElementViewsFolder‘ value. So next create folder ‘~/Views/Shared/ElementBlocks’ and add new view file named ‘FormContainerBlock’, use the Episerver ‘Block Template (Web Forms)‘ to add the view file. Delete the created .ascx.cs and .ascx.designer.cs files for the FormContainerBlock.ascx.

Extract the default implementation to a temporary location: open the EPiServer.Forms.zip file in your favorite archiving tool and extract the ‘Views\ElementBlocks\FormContainerBlock.ascx‘ to temporary location. Open the extracted file in a text editor, copy the file content and paste the text to the just created .ascx file in Visual Studio. Next change the ViewUserControl<FormContainerBlock> to use our custom class: SiteFormsContainerBlock and add using statement for the SiteFormsContainerBlock namespace.

Next we will add the custom properties as data-* properties to the html form element and use custom JavaScript to pull the values from the form when succesful submit is done and create the submission event to Google Tag Manager. See the added data-gtm-formname and data-gtm-formcategory in the below FormContainerBlock.ascx code file.

Version: 4.9.1. Modified: 20171030
<%@ Import Namespace="System.Web.Mvc" %>
<%@ Import Namespace="EPiServer.Web.Mvc.Html" %>
<%@ Import Namespace="EPiServer.Shell.Web.Mvc.Html" %>
<%@ Import Namespace="EPiServer.Forms" %>
<%@ Import Namespace="EPiServer.Forms.Core" %>
<%@ Import Namespace="EPiServer.Forms.Helpers.Internal" %>
<%@ Import Namespace="EPiServer.Forms.EditView.Internal" %>
<%@ Import Namespace="EPiServer.Forms.Implementation.Elements" %>
<%@ Import Namespace="SampleCustomFormContainer.Models.Forms" %>
<%@ Control Language="C#" Inherits="ViewUserControl<SiteFormsContainerBlock>" %>
var _formConfig = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<EPiServer.Forms.Configuration.IEPiServerFormsImplementationConfig>();
// require always our custom GTM JavaScript
<% if (EPiServer.Editor.PageEditing.PageIsInEditMode) { %>
<link rel="stylesheet" type="text/css" data-f-resource="EPiServerForms.css" href='<%: ModuleHelper.ToClientResource(typeof(FormsModule), "ClientResources/ViewMode/EPiServerForms.css")%>' />
<% if (Model.Form != null) { %>
<div class="EPiServerForms">
<h2 class="Form__Title"><%: Html.PropertyFor(m => m.Title) %></h2>
<h4 class="Form__Description"><%: Html.PropertyFor(m => m.Description) %></h4>
<%: Html.PropertyFor(m => m.ElementsArea) %>
<% } else { %>
<%–In case FormContainerBlock is used as a property, we cannot build Form model so we show a warning message to notify user–%>
<div class="EPiServerForms">
<span class="Form__Warning"><%: Html.Translate("/episerver/forms/editview/cannotbuildformmodel") %></span>
<% } %>
<% } else if (Model.Form != null) { %>
Using form tag (instead of div) for the sake of html elements' built-in features e.g. reset, file upload
Using enctype="multipart/form-data" for post data and uploading files
var validationCssClass = ViewBag.ValidationFail ? "ValidationFail" : "ValidationSuccess";
<%–Form will post to its own page Controller –%>
<% if (ViewBag.RenderingFormUsingDivElement) { %>
<%– *** data-gtm-formname and data-gtm-formcategory added compared to original Episerver implementation *** –%>
<div data-f-metadata="<%: Model.MetadataAttribute %>" class="EPiServerForms <%: validationCssClass %>" data-f-type="form" id="<%: Model.Form.FormGuid %>" data-gtm-formname="<%: Model.GtmFormName %>" data-gtm-formcategory="<%: Model.GtmCategoryName %>">
<%} else {%>
<%– *** data-gtm-formname and data-gtm-formcategory added compared to original Episerver implementation *** –%>
<form method="post" novalidate="novalidate"
data-f-metadata="<%: Model.MetadataAttribute %>"
data-gtm-formname="<%: Model.GtmFormName %>"
data-gtm-formcategory="<%: Model.GtmCategoryName %>"
enctype="multipart/form-data" class="EPiServerForms <%: validationCssClass %>" data-f-type="form" id="<%: Model.Form.FormGuid %>">
<%} %>
<%–Meta data, authoring data of this form is transfer to clientside here. We need to take form with language coresponse with current page's language –%>
<script type="text/javascript" src="<%: _formConfig.CoreController %>/GetFormInitScript?formGuid=<%: Model.Form.FormGuid %>&formLanguage=<%: FormsExtensions.GetCurrentFormLanguage(Model) %>"></script>
<%–Meta data, send along as a SYSTEM information about this form, so this can work without JS –%>
<input type="hidden" class="Form__Element Form__SystemElement FormHidden FormHideInSummarized" name="__FormGuid" value="<%: Model.Form.FormGuid %>" data-f-type="hidden" />
<input type="hidden" class="Form__Element Form__SystemElement FormHidden FormHideInSummarized" name="__FormHostedPage" value="<%: FormsExtensions.GetCurrentPageLink().ToString() %>" data-f-type="hidden" />
<input type="hidden" class="Form__Element Form__SystemElement FormHidden FormHideInSummarized" name="__FormLanguage" value="<%: FormsExtensions.GetCurrentFormLanguage(Model) %>" data-f-type="hidden" />
<input type="hidden" class="Form__Element Form__SystemElement FormHidden FormHideInSummarized" name="__FormCurrentStepIndex" value="<%: ViewBag.CurrentStepIndex ?? "" %>" data-f-type="hidden" />
<input type="hidden" class="Form__Element Form__SystemElement FormHidden FormHideInSummarized" name="__FormSubmissionId" value="<%: ViewBag.FormSubmissionId %>" data-f-type="hidden" />
<%= Html.GenerateAntiForgeryToken(Model) %>
<% if (!string.IsNullOrEmpty(Model.Title)) { %> <h2 class="Form__Title"><%: Model.Title %></h2> <% }%>
<% if (!string.IsNullOrEmpty(Model.Description)) { %> <aside class="Form__Description"><%: Model.Description %></aside> <% }%>
<% var statusDisplay = "hide";
var message = ViewBag.Message;
if (ViewBag.FormFinalized || ViewBag.IsProgressiveSubmit){
statusDisplay = "Form__Success__Message";
else if (!ViewBag.Submittable && !string.IsNullOrEmpty(message)) {
statusDisplay = "Form__Warning__Message";
if (ViewBag.IsReadOnlyMode)
<div class="Form__Status">
<span class="Form__Readonly__Message">
<%: Html.Translate("/episerver/forms/viewmode/readonlymode")%>
<%– area for showing Form's status or validation –%>
<div class="Form__Status">
<div class="Form__Status__Message <%: statusDisplay %>" data-f-form-statusmessage>
<%= message %>
<div data-f-mainbody class="Form__MainBody">
<% var i = 0;
var currentStepIndex = ViewBag.CurrentStepIndex == null ? 1 : (int)ViewBag.CurrentStepIndex;
string stepDisplaying;
foreach (var step in Model.Form.Steps) {
stepDisplaying = (currentStepIndex == i && !ViewBag.FormFinalized && (bool)ViewBag.IsStepValidToDisplay) ? "" : "hide"; %>
<section id="<%: step.ElementName %>" data-f-type="step" data-f-element-name="<%: step.ElementName %>" class="Form__Element FormStep Form__Element–NonData <%: stepDisplaying %>" data-f-stepindex="<%: i %>" data-f-element-nondata>
var stepBlock = (step.SourceContent as ElementBlockBase);
if(stepBlock != null)
Html.RenderContentData(step.SourceContent, false);
<!– Each FormStep groups the elements below it til the next FormStep –>
Html.RenderElementsInStep(i, step.Elements);
<% i++; } // end foreach steps %>
<% // show Next/Previous buttons when having Steps > 1 and navigationBar when currentStepIndex is valid
var currentDisplayStepCount = Model.Form.Steps.Count();
if (currentDisplayStepCount > 1 && currentStepIndex > 1 && currentStepIndex < currentDisplayStepCount && !ViewBag.FormFinalized) {
string prevButtonDisableState = (currentStepIndex == 0) || !ViewBag.Submittable ? "disabled" : "";
string nextButtonDisableState = (currentStepIndex == currentDisplayStepCount 1) || !ViewBag.Submittable ? "disabled" : "";
<% if (Model.ShowNavigationBar) { %>
<nav role="navigation" class="Form__NavigationBar" data-f-type="navigationbar" data-f-element-nondata>
<button type="submit" name="submit" value="<%: SubmitButtonType.PreviousStep.ToString() %>" class="Form__NavigationBar__Action FormExcludeDataRebind btnPrev"
<%: prevButtonDisableState %> data-f-navigation-previous>
<%: Html.Translate("/episerver/forms/viewmode/stepnavigation/previous")%></button>
// calculate the progress style onserverside
var currentDisplayStepIndex = currentStepIndex + 1;
var progressWidth = (100 * currentDisplayStepIndex / currentDisplayStepCount) + "%";
<div class="Form__NavigationBar__ProgressBar">
<div class="Form__NavigationBar__ProgressBar–Progress" style="width: <%: progressWidth %>" data-f-navigation-progress></div>
<div class="Form__NavigationBar__ProgressBar–Text">
<span class="Form__NavigationBar__ProgressBar__ProgressLabel"><%: Html.Translate("/episerver/forms/viewmode/stepnavigation/page")%></span>
<span class="Form__NavigationBar__ProgressBar__CurrentStep" data-f-navigation-currentStep><%:currentDisplayStepIndex %></span>/
<span class="Form__NavigationBar__ProgressBar__StepsCount" data-f-navigation-stepcount><%:currentDisplayStepCount %></span>
<button type="submit" name="submit" value="<%: SubmitButtonType.NextStep.ToString() %>" class="Form__NavigationBar__Action FormExcludeDataRebind btnNext"
<%: nextButtonDisableState %> data-f-navigation-next >
<%: Html.Translate("/episerver/forms/viewmode/stepnavigation/next")%></button>
<% } %>
<% } // endof if %>
<%– endof FormMainBody –%>
<% if (ViewBag.RenderingFormUsingDivElement ) { %>
<%} else{ %>
<% } %>
<% } %>

Last is the JavaScript file that will extract the data-* attribute values from the form when it is submitted.

if (typeof $$epiforms !== 'undefined') {
$$epiforms(document).ready(function myfunction() {
$$epiforms(".EPiServerForms").on("formsSubmitted", function (event) {
if (event.isFinalizedSubmission && event.isSuccess) {
// we have our custom dataLayer object, we could also have this code somewhere: window.dataLayer = window.dataLayer || []; and then use that
if (typeof dataLayer !== 'undefined') {
var currentForm = $$epiforms(this).get(0);
// using dom native methods, to support wider range of browsers that don't support the 'dataset'
var categoryName = currentForm.getAttribute("data-gtm-formcategory");
var formName = currentForm.getAttribute("data-gtm-formname");
console.log('Form submitted, GTM dataLayer values, category name:[' + categoryName + '] form name:[' + formName + ']');
'event': 'formSubmission',
'formCategory': categoryName,
'formName': formName

view raw


hosted with ❤ by GitHub

So this is how it looks in action

Custom GTM properties on own tab in the form container

In browser console we can see the values taken from data-* attributes and the GTM form submission event triggered.


Wrapping it up

As can be seen from the sample, it is quite easy to implement your own custom Episerver Forms container block but it is hack as you need to copy paste the orginal view code to your implementation and modify it. In the long run maintaining the Episerver Forms updates to the view code and synching those back to your custom code might become a nightmare or at least an extra step each time you update the Episerver Forms NuGet package(s).

As a side note you might have spotted in the view file code that Episerver supports rendering the form using HTML div-element. The sample code will inject the data-* attributes also to the div-element and the JavaScript works the same for that case too. To use div-element for the form you need to change the Forms.config value renderingFormUsingDivElement=”false” to value true. BUT when I did this to test the functionality the form submit failed because it is now doing GET instead of POST when submitting the form and endpoint is not found (I didn’t have time to dig this around this time, but will test it later and report it to Episerver support if it is an issue in Episerver implementation and not in my code).

7 thoughts on “Create custom Episerver Forms container

  1. Thanks a lot for your posts on Episerver forms! Thourough and full of the details I needed, really helpful.
    I used your tutorial to create a custom container allowing two columns, by adding an extra element area. Most of it works as intended, but I got a 500 respons when submitting the form, with the main message “EPiServerException: ContentReference: Input string was not in a correct format.]” I also have two custom elements in the container, but none of them with changes of block, controller or view, Is this something you have come across before and might have ideas about?

    1. Hi Anna, I haven’t encountered the issue you describe.

      But if you look in to the HTML source of the form there should be a hidden input called ‘__FormHostedPage’ and that has the “ContentReference” value of the page, like id 115. So that might be something that is not right currently (the string value cannot be parsed to ContentReference maybe, somethign has changed that value?)

      If you remove your custom elements from the container and submit, does it then work? If yes, then you know the issue is with your own custom elements and you should look into those, one of them is using ContentReference type property?

      What is the version of Episerver Forms you are using? There were some bugs in the version 4.24.3 (I also tested my sample for this post with the version 4.24.3 and it worked without issues) but version 4.25.0 is now released, so you might want to upgrade to that version (tested the original sample with this version too and it works).

      1. Thanks a lot for your answer. I tried to update to 4.25 and I also have a form without custom elements. I’ve forwarded the issue to Episerver who are looking into it right now. When I’ve reach a solution I will post it here.

  2. Hi , Thanks for giving such useful information.Can you please help me give any simple solution of creating a form in Visual Studio and we can use in episever further for beginner.

      1. Hey, Thanks for sharing this link. But i was looking for better view(UI) of form. How we can modify the view of Form

      2. So if you want to have custom view (view that renders the Episerver form) then you should read the documentation https://world.episerver.com/documentation/developer-guides/forms/creating-a-custom-form-block/ which tells the basics how you can have a custom UI for the Forms (the customization you do in the copy of FormContainerBlock.ascx is totally up to you). I don’t have sample about that. I would encourage you to post your question to Episerver World forums (Episerver Forms): https://world.episerver.com/forum/developer-forum/episerver-forms/ that way you have a chance to get tips from others that have customized the view UI.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s