“Code-Less” Site Building

New approach for Liferay sites implementation

Overview

For the many years Liferay theme was the only way for defining the portal appearance. Usually, when a new project started (or customer requested a new site with a brand new design) - development  began with a new theme creation. With themes it's possible to define the overall styling and the basic page structure. Also, we can define the configuration options with theme settings, and theme variations with color schemes.  We can import resources with theme's resource importer, or use the theme inheritance to build the hierarchies for related themes. 

Then, in the latest versions Liferay introduced a new approach for site creation. Custom themes were not required anymore. And, instead, a brand new features were introduced: Master Pages, Fragments and StyleBooks. Honestly, at the beginning I was a bit sceptical about such an approach. In my opinion, such features could successfully complement the custom theme, but not actually replace it.

But, recently we implemented two different sites for our customer (both of sites were content-based).

For the first one we used the well-known approach. We created a theme, defined the theme settings, and implemented the page structure in a theme (header/footer,etc.). But some time after, we were asked questions from the customer, like: how can I modify this thing or how can I modify that thing? Sometimes we explained how to make the changes, but sometimes we had to answer: “You can’t modify this, this is a part of the theme's code, we need to make code changes and re-deploy”. And the customer was surprised - “Why? Liferay is so flexible, why can't I make these changes directly on the portal?”. And added “We have code freeze, we’re not allowed to deploy anything”. For big clients sometimes there is too much bureaucracy, and changing a single line of code (even theme’s code) will lead to the entire cycle of deployments to DEV/QA/PROD, regression testing etc.

Considering this, we decided to build a new site with a new approach. We created custom fragments for header and footer, created Master Pages for basic page templates, implemented widget templates for navigation, and configured the search results page. Everything was ready in less than in a week, and no deployment was required. With the LAR files we could easily move the new site between the environments.

Site Implementation Approaches

As mentioned above, there are two ways for implementation the site's appearance: theme-based and theme-less. See the comparison below (pros highlighted as bold):

Theme-Based Approach

Theme-Less Approach

Page structure is staticly defined in theme
Page structure can be defined directly on portal (e.g. with Master Pages)
All code is stored in a single place (Git)
Code is scattered across different components on portal: fragments, widget templates, etc.
Version history is supported (considering theme's code is stored in Git)
No version history (e.g. can't revert widget template to a previous state after an unintentional corruption)
Deployment is required No deployment required, just configuration
Strong front-end development skills required for  Less development skills required, mostly configuration 
Anything can be styled in a theme Limited options for styling, fonts, colors, etc.
Becomes deprecated  Modern Liferay Features are used (Master Templates, Content Pages, Fragments, StyleBook)
Not recommended in most cases Recommended by Liferay

 

Each of approaches has it's own pros and cons, and can be used in different cases. But, generally "theme-less" one wins - because it's dynamic, recommended by Liferay, and uses the modern features, on which the platform development team invests the most of resources. 

"Code-Less" definition

"Code-less" here means that all the site implementation is performed directly on portal. No custom theme and deployment is required. But, some code is still needed: HTML/CSS/JS code for custom fragments, Freemarker code for custom widgets templates. Thus, it's a low-code, theme-less and deployment-less site implementation.

Sample site overview

As an example, we'll build a "Learning Center" site, similar to the https://learn.liferay.com/ 

Welcome page will display the welcome message, search bar and a link to Learning Center home page. There should be external links, language switcher and profile in the header. Footer will also have some links and social icons. 

Home page will display the left navigation for navigation between chapters/pages. Search will be placed to the header. There will be a breadcrumb and chapters navigation in the content section.

Chapter page should display the chapter information and page navigation in the content section.

Finally, the content page should display the actual content.

Header Implementation

Header for the site can be implemented as a custom Fragment. Fragments not only provide customizable pieces of content, but can also include embedded widgets or even drop zones, and have the configuration options.

Header's fragment HTML can be the implemented like this:

<div id="header" class="d-flex justify-content-between py-2 mx-2 mx-2">
    <div class="d-flex justify-content-start">
        <!-- Logo and Header-->
        <a href="/web/learning-center">
            <img alt="LC Logo" src="" class="h-75" data-lfr-editable-type="image" data-lfr-editable-id="lc-logo"
            />
            <h2 data-lfr-editable-type="rich-text" class="px-2" data-lfr-editable-id="lc-header">
                Header
            </h2>
        </a>
    </div>
    <div class="d-flex justify-content-end">
        <div class="px-2">
            <!-- Navigation and Search -->
            <lfr-drop-zone></lfr-drop-zone>
        </div>
        <div class="px-2">
            <!-- Language Switch -->
            [#if configuration.showLanguage]
                [@liferay_portlet["runtime"]
                portletName="com_liferay_site_navigation_language_web_portlet_SiteNavigationLanguagePortlet"/]
            [/#if]
        </div>
        <div class="px-2">
            <!-- User Account -->
            [#if configuration.showPersonalBar]
                [@liferay_portlet["runtime"]
                portletName="com_liferay_product_navigation_user_personal_bar_web_portlet_ProductNavigationUserPersonalBarPortlet"/]
            [/#if]
        </div>
    </div>
</div>

Within this small code snipped the following fragment features are used:

  • Editabled fields - for header logo/image, with data-lfr-editable-type attributes;
  • Embedded widgets - for language switcher and user account. Note: I use liferay_portlet["runtime"] tag library here, because these widgets are not available with <lfr-widget-xxx tag.
  • Embedded drop zone - for navigation and search section, to make them more dynamic.
  • Configuration options - for toggling the visibility of language/account widgets, see the configuration file.

Footer Implementation

For the footer we can also use a custom fragment. But we can make it even more "generic" by defining the footer's structure and adding the drop zones, see:

<footer id="footer" role="contentinfo" class="container-fluid py-2">
    <div class="row">
        <div class="col-md-2">
            <!-- Logo and Header-->
            <a href="/web/learning-center">
                <img alt="Liferay Logo" class="h-50" data-lfr-editable-type="image" data-lfr-editable-id="liferay-logo" />
                <h2 data-lfr-editable-type="rich-text" data-lfr-editable-id="liferay-header">Liferay</h2>
            </a>
        </div>
        <div class="col-md-8">
            <!-- Navigation -->
            <lfr-drop-zone></lfr-drop-zone>
        </div>
        <div class="col-md-2">
            <!-- Social Links -->
            <lfr-drop-zone></lfr-drop-zone>
        </div>
    </div>
</footer>

Here we also define the configurable logo image and title, and add two drop zones: for navigation and for social links.

Header Menu Implementation

External links in the header can be created as a "Link to URL" Liferay pages (with appropriate URL in page configuration). We can add a custom navigation menu "Top Links" and group those pages together here: 

Master Pages Implementation

Now it's time to introduce the Master Pages. Master Page Templates provide a way to define the page structure, which was previously a part of theme's implementation. Master page is a "basic template" for pages. Regular page templates and individual pages can be created then based on a master page. Master page has a pre-defined "DropZone" element, which can't be removed, but can be placed in any place on a page, together with the manually added elements. On the pages, created from master page, only the "DropZone" area is editable, other part of page is static, as it's defined in a master page.

"LC Basic Page" Implementation

We can create a "LC Basic Page" template with a custom header and footer fragments:

In both header and footer fragment we can select logo icon and type the heading text (using the fragment's editable fields). 

In the header fragment we can add a "Navigation Menu" widget to a fragment's DropZone, and configure it to display the "Top Links" navigation menu, created above.

For the footer we can add a grid, and place the editable links. In the right section we can add the pre-defined "Social" fragment, and configure the appropriate links. 

"LC Page with Left Nav" Implementation

We can create a new "LC Page with Left Nav" master page for the pages with the left navigation. We can make a copy of "basic" one, created above, and make the following adjustments:

  • add a grid into the middle section; 
  • add a "Navigation Menu"to the left column. Note: navigation should be configured to use a custom widget template "LEFT_NAV", created in the next section.
  • move DropZone to the right column;
  • add a "Breadcrumb" widget above the DropZone;
  • add a "Search Bar" widget to the header's DropZone, right to the navigation.

Widget Templates Implementation

Widget Templates are used for customization the portlet's appearance. The "Navigation Menu" widget has some pre-defined widget templates (which can be discovered in the "Global" site on portal). But when a new structure for navigation is required  - a custom widget template can be implemented. It's a best practice to create a custom template as a copy of out-of-the-box one, and make the custom changes on top of it.

"LEFT_NAV" Widget Template 

Left navigation should display the menu from the current page, and optionally display a "Go Back" button to navigate a level up in site navigation. It can be implemented like this

After implementation, this widget template should be selected in the "Navigation Menu" configuration on the "LC Page with Left Nav" master page.

"CHAPTER_NAV" Widget Template 

Chapter navigation should be shown inside the content of parent page, and display the navigation boxes with child / sub-child pages, like we see on the learn.liferay.com:

Code for chapter navigation can be implemented like this:

<div id="${navbarId}">
    <#if layoutNavItem.hasBrowsableChildren()>
        <div class="sections-wrapper">
            <#list layoutNavItem.getChildren() as nav_child>
                <div class="section-card">
                    <div class="autofit-row autofit-row-center">
                        <div class="autofit-col autofit-col-expand">
                            <a href="${nav_child.getURL()}">
                                <h4 class="sidebar title">${nav_child.getName()}</h4>
                            </a>
                            <#if nav_child.hasBrowsableChildren()>
                                <ul class="subsection">
                                    <#list nav_child.getChildren() as nav_sub_child>
                                        <li>
                                            <a href="${nav_sub_child.getURL()}">
                                                ${nav_sub_child.getName()}
                                            </a>
                                        </li>
                                    </#list>
                                </ul>
                            </#if>
                        </div>
                    </div>
                </div>
            </#list>
        </div>
    </#if>
</div>

 (see the complete version on GitHub).

Page Templates Implementation

As far, as we created already the required Master Pages and Widget Templates - we can start with the pages/content creation. But we can make a step forward to simplify the individual page creation process, and create page templates for specific pages (in our case - welcome, chapter, content and search templates):

For example, for the chapter template we can add a header, paragraph, and navigation menu (already configured to display the chapter navigation) to the content section (DropZone).

Then, on individual pages, we'll have to only change the values for the editable fields (but will be also able to add new elements, if needed). 

Site Pages Creation

Now it's time to setup the site pages and content. We can create site pages based on the page templates created in the previous chapter. We may have a page tree, like this:

Once pages are created - we'll finally see the resulting site:

(but do not judge the styling quality, I'm not a designer ?)

Style Books

With a custom StyleBook we can configure the styling options (colors, spacing, fonts, etc.) and define the styling according to the site's requirements. We may have different style books with different styling options, and apply them when we need to modify the site's appearance.

Note: style books load configuration options (token definitions) from a theme (default "Classic" theme in our "code-less" approach). When more configuration options are required - a custom theme is still needed, and custom token definitions can be defined in this case. 

Conclusions

We have created a sample site with a "code-less" approach, using the Liferay features:

  • Fragments;
  • Master Pages;
  • Navigation Menus;
  • Widget Templates;
  • Page Templates;
  • Content Pages;
  • Style Books.

We have compared two ways of site creation: "theme-based" and "theme-less". Each of them has it's own pros and cons. Theme-less approach is more modern and dynamic. And theme approach is more powerful. Actually, both approaches can be also used together: with a custom theme we can define the basic styling and custom options for style books; with master page templates we can define the portal page structure. 

Source code for templates:  https://github.com/liferay-apps/liferay-codeless/tree/master/modules/learning-center

See you on /dev/24

Enjoy ?

Vitaliy Koshelenko

Liferay Architect at Aimprosoft

v.koshelenko@aimprosoft.com

Blogs

Hi Vitaliy,

I found this article very interesting but have some things to add / question:

First of all because I think its most important, where did you get that the theme based approach will become deprecated? Or is this just a guess? (Then a footnote would be good).

As for the pro "All code is stored in a single place" I don't think that this is the case even with theme based, as we nowadays always have some fragments (in a toolkit project in git) and often some widget templates (also in a seperate git project). So in my opinion its as scattered as the theme-less approach.

I also wanted to add for the "no deployment" theme-less approach argument - Liferay reccomends to use the fragment toolkit project, which generates a deployable zip - yes one could argument that this is no deployment as it can be imported via the UI, but to me this is still some kind of deployment.

 

Also for some of our customers the theme based approach always wins, because of performance reasons - in the theme we can improve the reached performance in eg. a web vitals or google page speed test big times.

As for header and footer what we always add into our themes are theme settings for not rendering them - and we use eg. navigation & webcontent display widgets.

Please don't think of my comment a critique but as additional thoughts as I think it's a good base for some interesting discussions. (Actually we had some of these in the past few projects).

Manuel Manhart,

More and more new Liferay features are dedicated to empower developers with possibility to create content without a custom theme:

- "Addition of CSS classes to fragments from the Page Editor" from 7.4-ga34: https://liferay.dev/blogs/-/blogs/liferay-portal-7-4-ga34-and-liferay-commerce-4-0-ga34-release

- "Modify Theme CSS of Pages, without the need to deploy the Theme, with the new Theme CSS Client Extension" from 7.4-ga44: https://liferay.dev/blogs/-/blogs/liferay-portal-7-4-ga44-and-liferay-commerce-4-0-ga44-release which means, they prefer content-based approach over the theme-based.

Both of the features sound very nice. And while I do agree that it's clearly the intention of the devs of enabling to create a site without custom themes altogether I don't believe it's used widely (in the wild) soon.

I am still in the nessecity of creating child themes, theme settings and so on, as it's not easily possible to replace all of that yet.

I do believe as long as the JS framework keeps being a big monolith (even when distributed to many small files) and the SPA mode in some cases not being handleable its neccessary to be able do some magic in a custom theme :)

But in the last years there was a big shift from backend dev to frontend (devs and even editors) and that's a good thing as one can save many dev hours when thinking how a requirement can be tackled by using the (new) OOTB mechanisms.

Hi Manuel,

Thanks for your input, really appreciated.

The main idea of this post was to demonstrate that there is a new "code-less" approach for site building, besides the regular theme-based approach.

Liferay folks often recommend using Master Pages instead of custom theme, when possible (in community slack, on forums, etc.). I think, themes themselves will allway be there, and will never die... But some theme-related features are deprecated already (e.g. Resource Importer). Also, Liferay team invests more and more time and resources into Web Experience development (improving fragments, master pages, style books, etc.), while theme-related features (theme settings, color schemes) remain untouched.

As for code storage/version history - when changes are made directly on portal (with "code-less" approach) sometimes it's hard to reset the previous state, if something is broken. That's why I suggest keeping the version history of modified files on portal (e.g. in Git repository, even though they are not required for deployment).

As for deployment - it's more about possibility to avoid the deployment... We can use fragment toolkit for fragments deployment, we can also use site initializers to import widget templates and setup other content. But, if required, we can make the required changes directly on portal. With theme-based approach we still need to re-deploy a theme to apply the changes.

Actually, we can get the most, if we combine both approaches: we'll get the power of themes, and the dynamism of master pages. Custom theme can be used to define the styling elements, which then can be used inside fragments/templates. But, as for page structure - it's better to define it with a master page (and we can use a custom theme with disabled header/footer in theme settings). Everything should be as dynamic as possible (thus, require as less deployment as possible).

Thanks, Vitaliy