In this beginner tutorial you will create a simple web page based on a custom-built light module called hello-magnolia. Your light module will provide a page template and a custom quotation component for a web page. You will learn how to get content from content repository and display it on the page. You will develop the page using Magnolia CLI, an npm package which facilitates light development with Magnolia.

Magnolia CLI is available in several release versions. This tutorial has been checked against version 2.2.0.

Install Java

Magnolia needs a Java Runtime Environment (JRE) version 8. Type java -version in a terminal or command prompt. If the system reports a version number, Java is installed on your computer.
$ java -version
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)

If you don't have Java, install it:

Java is not pre-installed on Mac OS X 10.7 and later. Download the latest Java from Oracle.

On Windows you need a Java SE Development Kit (JDK). The Java Runtime Environment (JRE) is not enough because the Tomcat application server does not recognize it.

What is the difference?

  • JRE is for users who run Java programs on their computer.
  • JDK is for developers who write Java-based applications.

Download and install JDK. By default JDK is installed in C:\Program Files\Java\jdk1.8.0_xx\. You can choose another location.

Check JAVA_HOME environment variable

  1. Open the command prompt.
  2. Type set and press ENTER.
  3. Find JAVA_HOME in the command output and verify that the path points to your JDK installation directory, for example C:\Program Files\Java\jdk1.8.0_xx.
  4. If JAVA_HOME is missing or it points to the wrong directory, see Set JAVA_HOME environment variable below.

Set JAVA_HOME environment variable

  1. Right-click My Computer and select Properties.
  2. Go to the Advanced tab. 
    (In Windows 7+, right-click Computer and select Advanced System Settings, then Environment variables.)
  3. If the JAVA_HOME environment variable does not exist in User variables or System variables, create it:
    • User variables apply to the currently signed-in user only. Create JAVA_HOME here if you want it to apply only to the currently logged in user. These variables take precedence over system variables.
    • System variables apply to all users. Create JAVA_HOME here if you want it to apply to all users. You must be an administrator to modify a system environment variable. 
  4. Set the value of JAVA_HOME to the path of your JDK installation directory, for example C:\Program Files\Java\jdk1.8.0_xx.
  5. Optional step: Add the Magnolia bin directory to the PATH variable, for example C:\Program Files\magnolia\apache-tomcat-x.y\bin. Setting the PATH allows you to issue the Magnolia start and stop commands from anywhere without navigating to the installation directory first. Separate the path from existing paths with a semicolon ( ; ). If you do this, you also need to add CATALINA_HOME to environment variables. Set the value of CATALINA_HOME to the Tomcat installation directory, for example C:\Program Files\magnolia\apache-tomcat-x.y
  6. Click OK.
  7. Go back to Check JAVA_HOME environment variable above and test that the variable is found and has the correct value. You need to open a new command prompt since environment variables are session specific.

Alternatively you can set JAVA_HOME with a batch file. Add this line to /apache-tomcat/bin/magnolia_control.bat:

set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_xx

The set command creates the JAVA_HOME environment variable and sets its value to the JDK directory. The command is executed when Magnolia starts.

Download the latest Java from Oracle. The installation directory varies from one Linux system to another. For example, on Ubuntu Linux 10 the OpenJDK Runtime Environment is installed in /usr/lib/jvm/java-8-openjdk/jre by default.

Download the latest Java from Oracle. You can install it in any directory such as /usr/java .

Install Node.js

Magnolia CLI is an npm package providing a command line interface (CLI) tool to set up and facilitate light development with Magnolia. The Magnolia CLI tool runs on Node.js. If you do not have Node.js installed, go to Node.js and download and install the latest LTS version. 

To check the version of your node installation run the following command in a shell:

node -v

Install Magnolia CLI

Run the following command in a shell:
npm install @magnolia/cli -g

Depending on your permissions and the location where you have installed Node.js, you may have to execute the command above with root permissions. 
Without installation permissions you will notice messages such as npm ERR! in the shell. 
On Linux or OS-X to run this command as root use:

sudo npm install @magnolia/cli -g

If the installation is successful, you see the following or a similar output in the shell:

/usr/bin/mgnl -> /usr/lib/node_modules/@magnolia/cli/bin/mgnl.js

> spawn-sync@1.0.15 postinstall /usr/lib/node_modules/@magnolia/cli/node_modules/spawn-sync
> node postinstall

+ @magnolia/cli@2.2.0
added 209 packages in 11.40

If you already installed Magnolia CLI, update to the latest version:

npm update @magnolia/cli -g

Once you have installed Magnolia CLI, test the installation by running the following command in the shell:

mgnl help 

  Usage: mgnl <command> [options]

  A tool to setup and facilitate light development with Magnolia CMS


    -V, --version  output the version number
    -h, --help     output usage information


    jumpstart               download and setup a Magnolia CMS instance for development.
    start                   start up a Magnolia CMS instance. To stop it, enter CTRL+C
    add-availability        add component availability.
    build                   scan a node_modules folder for npm packages with the keyword "magnolia-light-module" (in package.json) and extract them to a directory of choice.
    create-component        create a component and optionally add availability for it.
    create-light-module     create a light module.
    create-page             create a page template.
    customize-local-config  extract "mgnl-cli-prototypes" folder and "mgnl-cli.json" file to customize CLI configuration.
    install                 install a light module from npm to the local Magnolia instance.
    search                  search for a light module on npm.
    tab-completion          install tab autocomplete feature for Bash, zsh or PowerShell
    help [cmd]              display help for [cmd]

  mgnl: 2.2.0 node: v6.11.0 os: darwin

Download and install a Magnolia bundle

For development and testing you need a running instance of Magnolia and a directory for:

  • The latest release of the Community Edition of Magnolia, bundled together with the Tomcat server.
  • The light-modules folder, in which you will develop the hello-magnolia module.

In this tutorial, we'll use the name magnolia for the directory.

Download Magnolia

Create the magnolia directory, cd into it, open a shell and enter the following command:

mgnl jumpstart

You are prompted to choose a Magnolia bundle, choose 3) magnolia-community-demo-webapp

The command:

  • Downloads the most recent version of the Magnolia bundle you selected (currently version 

    Error rendering macro 'artifact-resource-macro'

    com.sun.jersey.api.client.ClientHandlerException: A message body reader for Java class, and Java type class, and MIME media type application/octet-stream was not found

    ) into the /magnolia directory.
  • Creates a light-modules folder in the directory.
  • Changes the default value of the  magnolia.resources.dir  configuration property from ${magnolia.home}/modules to /magnolia/light-modules for both the Author and the Public instances of Magnolia.

Once the setup operation is complete, you should see a message similar to this one:

info Magnolia has been successfully setup for light development!
info You can now open a new terminal tab or window and start it up with the CLI command 'mgnl start'
info Magnolia will be ready after a few seconds at localhost:8080/magnoliaAuthor. Username and password is superuser

Below is the folder structure created after running the mgnl jumpstart command: 

 ├── apache-tomcat/ 
 │   ├── bin/ 
 │   ├── conf/ 
 │   ├── lib/ 
 │   ├── logs/ 
 │   ├── temp/ 
 │   └── webapps/ 
 │       ├── magnoliaAuthor/
 │       ├── magnoliaPublic/
 │       └── ROOT/
 ├── downloads/
 └── light-modules

Install and start Magnolia

In the parent directory of light-modules, enter:

mgnl start

The command installs and starts Magnolia. This is complete when you see a message like Server startup in 112737 ms . You can then access the UI of the Author instance.

Log into the Author instance

Go to http://localhost:8080/magnoliaAuthor  and log in to the Author instance with:

  • Username: superuser
  • Password: superuser

The App launcher is displayed:

The App launcher is the first page you see when you log into Magnolia. The launcher displays apps as boxes – also known as the app launcher tiles. A click on a tile will launch the associated app. Each tile has an icon and a label. The apps that have something in common belong to the same group.

Creating a simple page template

Create a module structure for the template

In this section you will create the folder structure of the hello-magnolia module.

  1. Go to the light-modules folder:
  2. Run the following command:

    mgnl create-light-module hello-magnolia

    The created structure:

    ├── decorations/
    ├── dialogs/
    │   ├── components/
    │   └── pages/
    ├── i18n/
    │   └──
    ├── includes
    ├── templates/
    │   ├── components/
    │   └── pages/
    └── webresources/

This is a typical Magnolia module structure where hello-magnolia is the name of your module.

When creating a name for a module, do not use spaces, accented characters such as é, à, ç, ä, öü or special characters such as slashes /\ and so on. The name must match the regular expression [a-zA-Z0-9-_].

Magnolia continuously scans the file system folder defined by the magnolia.resources.dir property. In this case it looks inside the light-modules folder, and registers your module(s) automatically. It detects new and modified templates, dialogs and (web)resources.
Click WEB DEV > Resource files in the App launcher and check that the hello-magnolia module structure has been registered:

The  icon in the Origin column indicates that the given resource is loaded from the file system.

Create the template

In this section you create a simple page template in your hello-magnolia module. The page template will be simple in that it will not yet allow you to add editable components to the page.

Start with a static page

  1. Copy the following HTML code and save it as /hello-magnolia/templates/pages/hello.html:

    <!DOCTYPE html>
    <html xml:lang="en" lang="en">
        <title>Starter Page</title>
        <link rel="stylesheet" type="text/css" href="../../webresources/css/hello-style.css" media="all" />
        <div class="container">
          <h1>hello works!</h1>
  2. Create a new css folder in  /hello-magnolia/webresources/ .  

  3. Copy the CSS below and save it as /hello-magnolia/webresources/css/hello-style.css. We reference this stylesheet in the HTML page and, later on, in a FreeMarker template script.

    @import url(//;
    body {
       padding: 20px;
       font-family: Raleway, Helvetica, Arial, Tahoma, Verdana, sans-serif;
       background-color: #1e5799;
       background-attachment: fixed;
    .container {
       width: 100%;
       max-width: 960px;
       margin: 0px auto;
       padding: 20px;
       box-sizing: border-box;
       background-color: white;
    main {
       padding: 20px;
       margin-bottom: 40px;
    h1 {
       font-size: 4em;
       font-family: Raleway, Helvetica, Arial, Tahoma, Verdana, sans-serif;
       font-weight: normal;
       background-color: #f6f6f6;
       padding: 30px 0 30px 15px;
       margin: 0;
    blockquote {
       margin-top: 65px;
    blockquote cite {
       font-size: 1.3em;
       color: #333;
    blockquote p {
       border: 5px solid #0066aa;
       border-radius: 12px;
       padding: 25px 15px;
       position: relative;
       background-color: #fff;
       margin-bottom: 30px;
    /** Add the bottom triangle for the quote bubble using CSS **/
    blockquote p:before, blockquote p:after {
       position: absolute;
       display: block;
       border-style: solid;
       content: "";
       height: 0;
       width :0;
       box-sizing: border-box;
    blockquote p:before {
       left: 20px;
       bottom: -30px;
       border-color: transparent #0066aa transparent transparent;
       border-width: 0 30px 30px 0;
    blockquote p:after {
       left: 29px;
       bottom: -15px;
       border-color: transparent #fff transparent transparent;
       border-width: 0 15px 15px 0;
  4. Check the result. You can do this in two ways:
    1. By requesting the HTML page via Magnolia. The page should be accessible under the following URL:
      (warning) Note that by requesting the page in this way you are requesting a static resource file of the Author instance of Magnolia. At this stage of development there is no page called hello created using the Pages app. You may check this by trying to get http://localhost:8080/magnoliaAuthor/hello.html . The page will not be available. 
    2. By opening the hello.html file directly with your web browser.
    The static page should be rendered similarly to this screenshot:

    Note that the browser tab header is "Starter Page", which is provided directly by the content of the <title> element in the static HTML page.

In the steps that follow you:

  • Turn the static HTML file into a template script file containing FreeMarker directives. The directives make it possible to retrieve content of some parts of the page from the JCR repository, parts such as the content of the <title> element.
  • Create a template definition. It makes the template available to Magnolia's authoring system.
  • Create a dialog definition. A dialog definition defines the editable page properties.

Create a page template script

Rename the static HTML document hello.html to hello.ftl. The extension .ftl turns the file into a processed FreeMarker script.

Edit the script so that the final result appears as follows (the list of changes is provided below):

<!DOCTYPE html>
<html xml:lang="${cmsfn.language()}" lang="${cmsfn.language()}">
    [ /]
    <link rel="stylesheet" type="text/css" href="${ctx.contextPath}/.resources/hello-magnolia/webresources/css/hello-style.css" media="all" />

    <div class="container">
      <h1>${content.windowTitle!content.title!} works!</h1>


The changes:

  1. Line 2: Replaced en with the ${cmsfn.language()} function to set the value of the lang attributes. This will make sure that the primary language of the page will be rendered correctly if you decide to localize your web page(s) into more languages.
  2. Line 4: Inserted [ /] as a new line. This directive adds toolbars and makes the page properties dialog available. You will create the definition for this dialog immediately after creating a page template definition.
  3. Line 5: Replaced the static words "Starter page" with the ${content.windowTitle!content.title!} directive. The content of the <title> element will then be retrieved from the JCR repository.
  4. Line 6: Changed the CSS reference to use ${ctx.contextPath}. When the page is created during the rendering process, we need to know the absolute path to the resource. This directive ensures that the path to the resource is full and correct on both the author and the public instances.
  5. Line 11: Replaced the word "hello" in the <h1/> element with the  ${content.windowTitle!content.title!} directive to render the first part of the page title dynamically using the content of page properties.

Create a page template definition

template definition gives the template a name and makes it available to the system. It also tells the system which script renders the content. We use YAML to create template definitions.

  1. Copy the template definition code below.

    title: hello
    templateScript: /hello-magnolia/templates/pages/hello.ftl
    renderType: freemarker
    dialog: hello-magnolia:pages/hello
    visible: true
  2. Save it to a new file /hello-magnolia/templates/pages/hello.yaml.

Create a dialog definition for page properties

Copy the following code to a file named hello.yaml in /hello-magnolia/dialogs/pages/:

  label: Page properties
    - name: tabMain
      label: hello
        - name: title
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          i18n: true
          label: title
        - name: windowTitle
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          i18n: true
          label: window title

    class: info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition
    class: info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition

A dialog defined like this allows you to add and edit the values of page properties such as windowTitle and title. The content of these properties are stored in the Magnolia JCR repository.

Use the template to create a page

Now you can use the page template to create a page in the Author instance. You can then publish the page to the Public instance.

  1. Go to Magnolia and open the Pages app:
  2. Create a new page called hello. Start this by clicking Add page from the Action bar on the right hand side:
  3. In the Add page dialog that appears choose the hello template as the page template and click Next:
  4. In the Page properties dialog, enter Hello Magnolia into the fields Title and Window title
  5. Click Save changes.
  6. Click Preview page on the right to preview your page.
  7. Click Publish. You can then access the page on the Public instance of your Magnolia installation via the URL
    http://localhost:8080/magnoliaPublic/hello.html .

(thumbs up) Congratulations! You've created your first Magnolia page built on a page template.

Now let's enhance the content of the page by adding an editable custom-built quotation component to the area just below the "Hello Magnolia works!" heading.

Creating areas and components

Areas and components help modularize a project and structure pages. Once defined, you can reuse areas and components in many page templates.

Let's create an area with one component to add quotations. At the end the whole page should look like this:

What is an area

Typically you organize pages into smaller elements called areas. Areas allow you to control the page layout and restrict what content editors can place inside the area. Areas are rendered as div elements in the HTML so you can style them with CSS. Areas also provide repeatability. An area template typically loops through the components inside it, rendering them one by one.

What is a component

A component is the smallest block of content that editors can edit, delete and move as a single unit. Think of a component as content that "belongs together". When you look at a typical Magnolia page, you can identify components easily with that rule of thumb. At its simplest, a component is just a heading and some text that go together.

Define an area

Areas are defined in the page template definition. You do not need to create a separate area definition file.

Edit the /hello-magnolia/templates/pages/hello.yaml template definition and define the main area by appending the following code to it:

    renderType: freemarker

Save the file. In the next step you specify the location of the main area within the page.

Specify the location of the area and include it in the page

You must place the main area just below the page heading. To do so, edit the /hello-magnolia/templates/pages/hello.ftl template script and add a <div/> element with the area just below the <h1/> element:

<!DOCTYPE html>
<html xml:lang="${cmsfn.language()}" lang="${cmsfn.language()}">
    [ /]
    <link rel="stylesheet" type="text/css" href="${ctx.contextPath}/.resources/hello-magnolia/webresources/css/hello-style.css" media="all" />

    <div class="container ">
      <h1>${content.windowTitle!content.title!} works!</h1>
      <div class="main">
         [@cms.area name="main"/]


The FreeMarker expression [@cms.area name="main"/] includes the area. The <div class="main"> element wraps the expression for styling purposes.

Make sure that the FreeMarker expression [@cms.area name="main"/] is not included again at the end of the hello.ftl template script.

Create the quotation component

Make sure you are at the root of your module (/magnolia/light-modules/hello-magnolia). Use the following CLI command to create the quotation component and make it available in the main area of the page template:

mgnl create-component quotation -a pages/hello@main

The command creates the following files:

  • /hello-magnolia/dialogs/components/quotation.yaml
    This definition configures the elements of the dialog.
  • /hello-magnolia/templates/components/quotation.yaml
    This definition gives the component a name and makes the component available to the system.
  • /hello-magnolia/templates/components/quotation.ftl
    The template script renders the content of the component.

The command also modifies the /hello-magnolia/templates/pages/hello.yaml file, in which it adds the availability of the quotation component in the main area by appending the following piece of code to it:

        id: hello-magnolia:components/quotation

Configure the dialog fields

The CLI command mgnl create-component creates a dialog definition with field types which you do not need. Open the dialog definition file /hello-magnolia/dialogs/components/quotation.yaml for editing, and edit it to appear as follows:

  label: quotation
    - name: tabMain
      label: Main
        - name: citedPerson
          class: info.magnolia.ui.form.field.definition.TextFieldDefinition
          label: Cited Person

        - name: quotation
          class: info.magnolia.ui.form.field.definition.RichTextFieldDefinition
          label: Quotation
    class: info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition
    class: info.magnolia.ui.admincentral.dialog.action.CancelDialogActionDefinition

Be careful with the type of whitespace in this code. All the whitespace to the left of the text should be formed using the SPACE character, not the TAB character. For more details on this topic, see the 6.1. Indentation Spaces section in YAML v1.2 specification.

The key content elements in the above dialogue definition – citedPerson and quotation – are stored in the JCR repository and rendered in the page by the following code, the component's script.

Configure the component's template script

The FreeMarker script /hello-magnolia/templates/components/quotation.ftl created by the mgnl create-component command contains commands with little or no use in the context of this tutorial. Open the file for editing and save it only with the following code:

[#if content.quotation?has_content]
    [#if content.citedPerson?has_content]<cite>${content.citedPerson}</cite>[/#if]

The final structure of your hello-magnolia module in the light-modules folder should look like this:

├── decorations/
├── dialogs/
│   ├── components/
│   │   └── quotation.yaml
│   └── pages/
│       └── hello.yaml
├── i18n/
│   └──
├── includes
├── templates/
│   ├── components/
│   │   ├── quotation.ftl
│   │   └── quotation.yaml
│   └── pages/
│       ├── hello.ftl
│       └── hello.yaml
└── webresources/
    └── css/
        └── hello-style.css

Add the component to a page

Now you can add a quotation component to the hello page with the text and the author of a quotation.

Open the hello page in the Pages app and click the New main component green bar. Enter your favorite quotation in the dialog:

After saving your quotation, go back to AdminCentral and click Tools > JCR to check that the content of the text fields in the quotation dialog has been stored in the JCR repository for the main area of the hello page:

After you publish the page, access the final result on the Public instance and see the content of the quotation component in the page:

(thumbs up) Congratulations.

You now know the basics of Magnolia templating. Using these techniques you can build websites where non-technical authors can manage content using dialogs.

Further reading

#trackbackRdf ($trackbackUtils.getContentIdentifier($page) $page.title $trackbackUtils.getPingUrl($page))