Creating the Template VTL File

Last Updated: May 24, 2022
documentation for the dotCMS Content Management System

The template VTL (Velocity Template Language) file is a theme's backbone, and the most basic requirement for the creation of a new theme. Simply place a folder containing a template.vtl file into a site's application/themes directory, and dotCMS will detect a new theme, taking the theme's name from the folder.

The quickest way to get started building your own custom template.vtl is to use the default System theme's as a starting point, adding VTL includes to replace layout placeholders. An example of one way this might look can be found at the bottom of the page.

Velocity Variables

Template VTLs are able to call two important pre-defined Velocity variables, pointing to theme and layout data:

  • $dotTheme
  • $dotThemeLayout

Discussion of these two variables will be broken up into four subsections, in view of important nested properties.

Key Paths and More: $dotTheme

$dotTheme contains basic information about the theme:

PropertyData
$dotTheme.titleThe theme's name, drawn from its folder.
$dotTheme.pathThe directory containing the theme — e.g. /application/themes/THEME_NAME/.
$dotTheme.htmlHeadtrue or false depending on whether the folder contains html_head.vtl.
$dotTheme.templatePathThe path to the template.vtl file asset.

The Big Picture: $dotThemeLayout

$dotThemeLayout contains various layout properties.

PropertyData
$dotThemeLayout.titleThe title of the template or custom layout.
$dotThemeLayout.headertrue or false depending on the template or layout's settings.
$dotThemeLayout.footertrue or false depending on the template or layout's settings.
$dotThemeLayout.bodyContains body layout data; see below.
$dotThemeLayout.sidebarContains sidebar layout data; see below.

The two properties .body and .sidebar are special; both contain additional nested properties. In the system template.vtl, these two are given aliases for ease of reference:

## Main column
#set( $mainColumn = $dotThemeLayout.body )
## Sidebar
#set( $sidebar = $dotThemeLayout.sidebar )

We shall here consider these aliases to be a matter of convention.

The Body: $dotThemeLayout.body or $mainColumn

$mainColumn contains only a single array: $mainColumn.rows. Each individual $row within contains the $row.columns property, an array of individual $column elements.

A pair of nested #foreach loops can iterate through the whole structure:

#foreach( $row in $mainColumn.rows )
    <!-- row html -->
    #foreach( $column in $row.columns )
        <!-- column html -->
        $render.eval($column.draw())
        <!-- column html after content -->
    #end
    <!-- html concluding the row -->
#end

At the innermost loop lies a series of $column structures, which have the following properties:

PropertyData
$column.leftA number from 0 to 11, indicating horizontal position based on $mainColumn's 12 divisions, counting from the left.
$column.leftOffsetSame as $column.left, but indexed from 1 to 12 instead of 0 to 11.
$column.widthA value from 1 to 12, indicating the column's width in terms of the number of $mainColumn's 12 horizontal divisions used.
$column.widthPercentSimilar to .width, but represented as a percentage of the $mainColumn.
$column.styleClassLists HTML class attributes assigned through the layout editor.
$column.previewEquivalent to $column.isPreview(); yields true or false.
$column.draw()Generates a #parsecontainer macro for the column, which can be read by $render.eval() — a method of the RenderTool Viewtool.
$column.containersContains additional object data; see below.

Each $container in $column.containers possesses:

PropertyData
$container.identifierA reference assigned to each element on the basis of its publishing bundle.
$container.uuidAn index number to distinguish between elements with the same identifier.

$sidebar contains the following properties:

PropertyData
$sidebar.widthA value of small, medium, or large, as selected in the template editor.
$sidebar.widthPercentA percentage of the viewport space, corresponding to $sidebar.width — either 20, 30, or 40.
$sidebar.locationEither left or right, as defined in the template editor.
$sidebar.previewEquivalent to $sidebar.isPreview(); yields true or false.
$sidebar.draw()Generates a #parsecontainer macro for the sidebar, which can be read by $render.eval() — a method of the RenderTool Viewtool.
$sidebar.containersContains additional object data; identical to $column.containers, in the previous section.

Including Other VTLs in template.vtl

As with any case of including a VTL file, your html_head.vtl, header.vtl, and footer.vtl can be included with a call to the #dotParse macro, albeit with one small difference: They will require the $dotTheme.path variable in their file path.

Here are examples:

Including the HTML Head

#if($dotTheme.htmlHead)
    #dotParse("${dotTheme.path}html_head.vtl")
#end

Including the Header

#if($dotThemeLayout.header)
    #dotParse("${dotTheme.path}header.vtl")
#end

Including the Footer

#if($dotThemeLayout.footer)
    #dotParse("${dotTheme.path}footer.vtl")
#end

Example: Complete template.vtl

Below is an example of a functional template.vtl file, based on the System theme VTL. Bootstrap-style classes used throughout correspond to styles defined in the original's head; in the example below, those definitions — and other head elements — are assumed to have been moved to a html_head.vtl file in order to illustrate its inclusion and reduce screen clutter.

<!doctype html>
<html lang="en">

    <head>
        ############################
        ## HTML_HEAD (IF PRESENT) ##
        ############################
        #if($dotTheme.htmlHead)
            #dotParse("${dotTheme.path}html_head.vtl")
        #end        
        #set($utilClass = $dotPageContent.title.toLowerCase().replace(' ', '-'))
    </head>

    <body id="$utilClass" #if($EDIT_MODE)class="edit-mode"#end>

        <div class="body-wrapper">  
            #########################
            ## HEADER (IF PRESENT) ##
            #########################
            #if($dotThemeLayout.header)
                <header>
                    #dotParse("${dotTheme.path}header.vtl")
                </header>
            #end

            #############
            ## ALIASES ##
            #############
            ## Main column
            #set($mainColumn = $dotThemeLayout.body)
            ## Sidebar
            #set($sidebar = $dotThemeLayout.sidebar)

            ###############################
            ## LEFT SIDEBAR (IF PRESENT) ##
            ###############################
            #if ($sidebar && $sidebar.location != "")

                #if ($sidebar.width == 'small')
                    #set ($sidebarColumn1Span = "col-sm-2")
                    #set ($sidebarColumn2Span = "col-sm-10")
                #elseif ($sidebar.width == 'medium')
                    #set ($sidebarColumn1Span = "col-sm-3")
                    #set ($sidebarColumn2Span = "col-sm-9")
                #elseif ($sidebar.width == 'large')
                    #set ($sidebarColumn1Span = "col-sm-4")
                    #set ($sidebarColumn2Span = "col-sm-8")
                #else
                    #set ($sidebarColumn1Span = "")
                    #set ($sidebarColumn2Span = "")
                #end

                <div class="container">
                    <div class="grid">
                    #if ($sidebar.location == "left")
                        <div class="$sidebarColumn1Span">
                            ## Draw the column content
                            $render.eval($sidebar.draw())
                        </div><!--/div sidebar left-->
                        <div class="$sidebarColumn2Span">
                    #else
                        <div class="$sidebarColumn2Span">
                    #end
            #end

            #########################################
            ## ADDING THE ROWS FOR THE MAIN COLUMN ##
            #########################################  
            #if ($mainColumn.rows)

                #set($rowCount = 0)
                #foreach($row in $mainColumn.rows)
                    #set($rowCount = $rowCount + 1)
                    #set($rowLeftOffset = 1)
                    ## Each row has a number of columns, using bootstrap grid layout
                    #foreach($column in $row.columns)
                        #if($velocityCount == 1)

                        <section id="section-$!{rowCount}" class="section $!{row.styleClass}">
                            <div class="container">
                                <div class="grid">
                        #end

                        ## Define width by the column settings
                        #set ($currentColumnSpan = "col-lg-${column.width}")
                        #set ($offset = 0)

                        ## Set the bootstrap offset of each column based on the row's left offset. 
                        #if ($rowLeftOffset == $column.leftOffset)
                            #set($columnOffset = "")
                        #else
                           #set($offset = $column.leftOffset - $rowLeftOffset)
                           #set($columnOffset = "offset-lg-${offset}")
                        #end
                        #set($rowLeftOffset = $rowLeftOffset + $column.width + $offset)

                                    <div class="$currentColumnSpan $columnOffset $!{column.styleClass}">
                                        ## Draw the column content
                                        $render.eval($column.draw())
                                    </div><!--/Column-->

                        #if($velocityCount == $row.columns.size())
                                </div><!--/row-->
                            </div><!--/container-->
                        </section><!-- /row-wrapper-->
                        #end
                    #end
                #end
            #end

            ################################
            ## RIGHT SIDEBAR (IF PRESENT) ##
            ################################
            #if ($sidebar && $sidebar.location != "")
                #if ($sidebar.location == "left")
                    </div><!--/div columns-->
                #else
                    </div><!--/div columns-->
                    <div class="$sidebarColumn1Span">
                        ## Draw the column content
                        $render.eval($sidebar.draw())
                    </div><!--/div sidebar right-->
                #end
                </div><!--/div row-->
            </div><!-- /container-->
            #end
        </div><!-- /body-wrapper -->

        #########################
        ## FOOTER (IF PRESENT) ##
        #########################
        #if($dotThemeLayout.footer)
            <footer>
                #dotParse("${dotTheme.path}footer.vtl")
            </footer>
        #end
    </body>
</html>

On this page