Custom layout: Core functionality (Welcome)
A detailed guide to the key features, variables, and templating structure available in custom layouts
If you’ve already created your first custom layout using our getting started guide, this article takes you one step further. Here, we break down the core building blocks of the Welcome app’s custom layout system, covering available variables, feature flags, templating syntax, and how to use widgets and components effectively.
Use this guide as your go-to reference when shaping more polished, feature-rich layouts.
Jump to
- Handlebars templating
- Variables
- Conditional visibility
- Features
- Widgets and components
- Best practices
Handlebars templating
The Welcome app uses Handlebars as its templating engine. This allows you to:
- Insert variable values:
{{variable}} - Insert unescaped HTML:
{{{variable}}} - Use conditional logic:
{{#if variable}}...{{else}}...{{/if}} - Loop through arrays:
{{#each array}}...{{/each}} - Use custom helpers like audience targeting:
{{#audience "guid"...{{/audience}}
Examples:
<!-- Display the username -->
<h1>Hello, {{username}}!</h1>
<!-- Insert an unescaped background URL -->
<div style="background-image: url({{{background}}})"></div>
<!-- Conditional rendering -->
{{#unless custom.greeting.hide}}
<div class="greeting">
<a365widget data-widget="greeting"></a365widget>
</div>
{{/unless}}
<!-- Loop through content items -->
{{#each content}}
<div class="item">
<h2>{{title}}</h2>
<p>{{description}}</p>
</div>
{{/each}}
Variables
The following variables are available for use in your Handlebars templates.
| Variable | Type | Description |
webPartId |
string | Unique identifier for the web part instance |
userphoto |
string | URL of the user's profile picture |
username |
string | Display name of the user |
sourceUrl |
Nullable<string> | Source URL if applicable |
email |
string | Email address of the user |
audience |
string[] | Array of audience GUIDs the user belongs to |
background |
Nullable<string> | URL of the background image |
custom |
Record<BuiltInFeatures, Record<string, unknown>> | Custom variables defined in the layout |
content |
Slide[] | Array of content slides to display |
The content variable is an array of Slide objects with the following properties:
| Property | Type | Description |
id |
string | Unique identifier for the slide |
type |
string | The type of content (from ContentSlide or ContentNews) |
title |
string | The title of the slide |
subtitle |
string | The subtitle or secondary text for the slide |
background |
string | The URL of the background image for the slide |
ctaUrl |
string | Call-to-action URL (link destination when slide is clicked) |
ctaText |
string | Call-to-action text (button or link text) |
altText |
string | Alternative text for the slide image (for accessibility) |
custom |
Record<string, unknown> |
Optional custom properties specific to the slide |
Conditional visibility
Any <variable> - a custom variable you define, or a built-in override of an existing field - can be shown or hidden depending on the current value of another feature setting. This lets you build layouts where one option appears only when a related option is set in a certain way.
Add a showWhen attribute to the <variable> tag:
<variable feature="content" name="cycleSeconds" type="number"
label="Seconds between slide changes"
showWhen="content.cycle" />
Syntax
| Property | Meaning |
feature.name |
Truthy check - shown when that value is set / true / non-empty. |
feature.name==value |
Exact match - shown when the value equals value (compared as text). |
condA&&condB |
Both conditions must be true (AND). |
condA||condB |
Either condition must be true (OR). |
You can reference:
- Another custom variable, e.g.
content.myOtherVariable==foo - A built-in/OOTB value, e.g.
time.timeDisplay==custom,content.kind==video - Values can be chained - variable B's
showWhencan depend on variable A, which itself has its ownshowWhen.
Examples
<variable feature="time" name="acc-timeDisplayDate"
showWhen="time.timeDisplay==custom" />
<variable feature="content" scope="slide" name="acc-slideOverlay"
showWhen="content.kind==video" />
The first field only appears once the Time feature's display mode is set to "Custom." The second only appears on slides of type "video."
Tip: New to declaring your own fields? See Custom variables for how to define a <variable> in the first place.
Features
Features are enabled via data attributes on the metadata template. Each feature adds specific functionality to your layout:
| Feature | Attribute | Description |
| Greeting | data-feature-greeting="true" |
Enables personalised greeting functionality |
| Time | data-feature-time="true" |
Enables time display functionality |
| Weather | data-feature-weather="true" |
Enables weather information functionality |
| Search | data-feature-search="true" |
Enables search functionality |
| Slides | data-feature-slider="true" |
Enables slides functionality (legacy) |
| Content | data-feature-content="true" |
Enables content display (slides + news) |
| Background | data-feature-background="true" |
Enables background image functionality |
Widgets and components
Widgets are high-level UI elements that combine multiple components:
<a365widget data-widget="greeting"></a365widget>
<a365widget data-widget="time"></a365widget>
<a365widget data-widget="weather"></a365widget>
<a365widget data-widget="search"></a365widget>
<a365widget data-widget="content"></a365widget>
Components are lower-level UI elements that can be used independently:
<a365component data-component="greeting"></a365component>
<a365component data-component="photo"></a365component>
<a365component data-component="time"></a365component>
Best practices
- Keep only one instance of
<style>,<script>, and<section>tags in the layout - Avoid using HTML IDs; prefer classes instead
- If an ID is necessary, ensure it has a unique postfix, e.g.,
<div id="alert-{{webPartId}}"></div> - Clean up resources in the
onDispose()function to avoid memory leaks - Use the
.SCOPEclass token to target elements outside the layout scope - Only enable features that are actually used in the layout
- Use localized labels for better user experience
- Do not try to access or style elements outside of the template scope
- Avoid placing markup outside of template tags