---
title: Render multi-column form container block
slug: cms-12/docs/rendering-a-multi-column-form-container-block
docTags: 
createdAt: 2026-08-10T15:55:58.000Z
---

:::hint{type="info"}
**Note**
[Optimizely Forms](docId:3SbSlJZIS_3iFmghfQA3J) is only supported by MVC-based websites and HTML5-compliant browsers.
:::

The form container block renders elements line-by-line by default. Using a grid system like Bootstrap, you can customize the rendering process to display the form in a multi-column layout.

1. Add predefined breakpoints for the bootstrap grid.

```csharp
public static class FormDisplayOptionTags {
  public static readonly string[] FullWidth = new string[] { "span12", "col-xs-12", "col-sm-12", "col-md-12", "col-lg-12" };
  public static readonly string[] ThirdQuaterWidth = new string[] { "span9", "col-xs-9", "col-sm-9", "col-md-9", "col-lg-9" };
  public static readonly string[] TwoThirdsWidth = new string[] { "span8", "col-xs-8", "col-sm-8", "col-md-8", "col-lg-8" };
  public static readonly string[] HalfWidth = new string[] { "span6", "col-xs-6", "col-sm-6", "col-md-6", "col-lg-6" };
  public static readonly string[] OneThirdWidth = new string[] { "span4", "col-xs-4", "col-sm-4", "col-md-4", "col-lg-4" };
  public static readonly string[] OneQuaterWidth = new string[] { "span3", "col-xs-3", "col-sm-3", "col-md-3", "col-lg-3" };
}
```

2. Optionally, add more display options corresponding to these breakpoints:

```csharp
var options = ServiceLocator.Current.GetInstance();
options.Add("full", "/displayoptions/full", Global.ContentAreaTags.FullWidth, "", "epi-icon__layout--full")
       .Add("wide", "/displayoptions/wide", Global.ContentAreaTags.TwoThirdsWidth, "", "epi-icon__layout--two-thirds")
       .Add("narrow", "/displayoptions/narrow", Global.ContentAreaTags.OneThirdWidth, "", "epi-icon__layout--one-third")
       ...;
```

3. Create the `FormContentAreaRenderer` class to get content area items style and calculate the item width necessary for the layout.

```csharp
[ServiceConfiguration(ServiceType = typeof (FormContentAreaRender), Lifecycle = ServiceInstanceScope.Singleton)]
public class FormContentAreaRender: ContentAreaRenderer {
  private IContent _currentContent;

  /// Get css of a content area item
  public string GetItemCssClass(HtmlHelper html, ContentAreaItem areaItem) {
    var tag = GetContentAreaItemTemplateTag(html, areaItem);
    var baseClasses = base.GetContentAreaItemCssClass(html, areaItem);
    return $ "block {GetTypeSpecificCssClasses(areaItem)} {tag} {baseClasses}";
  }

  /// Get layout width of a content area item
  public int GetColumnWidth(HtmlHelper html, ContentAreaItem item) {
    var tag = GetContentAreaItemTemplateTag(html, item);
    return GetColumnWidth(tag);
  }

  private string GetTypeSpecificCssClasses(ContentAreaItem contentAreaItem) {
    var content = GetCurrentContent(contentAreaItem);
    var cssClass = content?.GetOriginalType().Name.ToLowerInvariant() ?? string.Empty;
    var customClassContent = content as ICustomCssInContentArea;
    if (customClassContent != null && !string.IsNullOrWhiteSpace(customClassContent.ContentAreaCssClass)) {
      cssClass += $ " {customClassContent.ContentAreaCssClass}";
    }
    return cssClass;
  }

  private IContent GetCurrentContent(ContentAreaItem contentAreaItem) {
    if (_currentContent == null || !_currentContent.ContentLink.CompareToIgnoreWorkID(contentAreaItem.ContentLink)) {
      _currentContent = contentAreaItem.GetContent();
    }
    return _currentContent;
  }

  /// Get the width of a css tag (bootstrap column width)
  private int GetColumnWidth(string tag) {
    if (Global.FormDisplayOtionTags.FullWidth.Contains(tag))

    {
      return 12;
    }
    if (Global.FormDisplayOtionTags.ThirdQuaterWidth.Contains(tag)) {
      return 9;
    }
    if (Global.FormDisplayOtionTags.TwoThirdsWidth.Contains(tag)) {
      return 8;
    }
    if (Global.FormDisplayOtionTags.HalfWidth.Contains(tag)) {
      return 6;
    }
    if (Global.FormDisplayOtionTags.OneThirdWidth.Contains(tag)) {
      return 4;
    }
    if (Global.FormDisplayOtionTags.OneQuaterWidth.Contains(tag)) {
      return 3;
    }
    return 12;
  }
}
```

4. Define `RenderFormElements` extension method for `HtmlHelper`.

```csharp
static Injected _formContenAreaRender;

///
/// Renders form elements
///
///Instance of HtmlHelper class
///Form element collection
public static void RenderFormElements(this HtmlHelper html, int currentStepIndex, IEnumerable elements) {
  FormContainerBlock model = (FormContainerBlock) html.ViewData.Model;
  if (model == null) {
    return;
  }
  /// TODO: calculate element width and group elements into rows
  /// We use rows to keep the layout unbroken since elements have different heights
  int rowWidthState = 0;
  var elementsInfos = elements.Select(element => {
    var areaItem = model.ElementsArea.Items.FirstOrDefault(i => i.ContentLink == element.SourceContent.ContentLink);
    var columnWidth = _formContenAreaRender.Service.GetColumnWidth(html, areaItem);
    rowWidthState += columnWidth;
    return new {
      ContentAreaItem = areaItem,
        RowNumber = rowWidthState % 12 == 0 ? rowWidthState / 12 - 1 : rowWidthState / 12
    };
  });

  var rows = elementsInfos.GroupBy(a => a.RowNumber, a => a.ContentAreaItem);
  foreach(var row in rows) {
    // start of new row
    html.ViewContext.Writer.Write("<div class=\"row row-" + row.Key + "\">");
    foreach(var item in row) {
      IContent content = item.GetContent();
      if (content == null || content.IsDeleted) {
        continue;
      }
      var cssClasses = _formContenAreaRender.Service.GetItemCssClass(html, item);
      // start of conten area item
      html.ViewContext.Writer.Write($"<div class=\"{cssClasses}\">");

      if (content is ISubmissionAwareElement) {
        var submissionAwareElement = (content as IReadOnly).CreateWritableClone() as IContent;
        (submissionAwareElement as ISubmissionAwareElement).FormSubmissionId = html.ViewBag.FormSubmissionId;
        html.RenderContentData(submissionAwareElement, false);
      } else {
        html.RenderContentData(content, false);
      }
      // end of content area item
      html.ViewContext.Writer.Write("</div>");
    }
    // end of row
    html.ViewContext.Writer.Write("</div>");
  }
}
```

5. Edit the view of `FormContainerBlock` (*FormContainerBlock.cshtml*) to use the new render method.
   Replace:

```csharp
<%@ Import Namespace="EPiServer.Forms.EditView.Internal" %>
```

with:

```csharp
<%@ Import Namespace="EpiserverSite2.Helpers" %>
```

6. `ICustomCssInContentArea` interface for adding a custom CSS for form element.

```csharp
public interface ICustomCssInContentArea {
  string ContentAreaCssClass {
    get;
  }
}
```

The following shows a form rendered in view mode.

:::hint{type="info"}
**Note**
A form can appear differently between edit view and view mode because in view mode, form elements are grouped into rows and columns to keep the layout unbroken. If you want a consistent look between edit view and view mode, create a custom content area renderer and apply it for the whole site.
:::

:::hint{type="warning"}
**Limitation**
Currently, for simplicity, breakpoints are fixed for bootstrap grid, so 1/3 is 1/3 across screens. You can specify that 1/3 should be on the desktop and 12/12 should be on smaller screens. However, modifying the form container block template (*FormContainerBlock.cshtml*) can be a little tricky.
:::

See also: blog post, [Create custom Forms container](https://swapcode.wordpress.com/2019/03/24/create-custom-episerver-forms-container/)
