Custom layout: Advanced functionality explained
A detailed guide to advanced functionality, including custom variables and audience targeting, available in custom layouts
If you’re ready to go beyond the core templating features of Welcome, this article covers the advanced tools available to custom layout developers. You’ll learn how to use dynamic metadata from SharePoint, set up audience targeting, hook into layout lifecycle events, and safely apply scoped styling using .SCOPE
.
This guide is ideal for those building polished, flexible, and dynamic layouts for the Welcome app.
Jump to
- Settings and custom managed metadata fields
- Custom variables
- Audience targeting
- Lifecycle hooks
- JavaScript context
- Understanding the .SCOPE pseudo-class
Settings and custom managed metadata fields
The <settings>
section allows you to define custom settings for your layout, including custom managed metadata fields that will be loaded for dynamic content.
Basic structure:
<settings>
<content>
<fields>
<field name="FieldName1" />
<field name="FieldName2" />
</fields>
</content>
</settings>
Custom managed metadata fields
Custom managed metadata fields allow you to load additional metadata from SharePoint lists or libraries where your content is stored. These fields should be mapped to a tenant-scoped managed property.
We recommend reading the following Microsoft resources before proceeding:
- Overview of crawled and managed properties in SharePoint Online
- How Do Site Columns Become Managed Properties - Thus Available for Search
How to define custom fields
- Add a
<field>
element inside the<fields>
section - Set the
name
attribute to the internal name of the SharePoint managed property
Example:
<settings>
<content>
<fields>
<field name="ShowInWelcome" />
<field name="Category" />
<field name="Priority" />
</fields>
</content>
</settings>
Accessing custom fields in layouts
Custom fields can be accessed in the layout using the content[i].custom
object:
{{#each content}}
{{#if custom.ShowInWelcome}}
<div class="item">
<h3>{{title}}</h3>
{{#if custom.Category}}
<span class="category">Category: {{custom.Category}}</span>
{{/if}}
{{#if custom.Priority}}
<span class="priority priority-{{custom.Priority}}">{{custom.Priority}}</span>
{{/if}}
</div>
{{/if}}
{{/each}}
Note: custom managed metadata fields are only supported for dynamic content (news and pages) loaded from SharePoint. They are not supported for static content defined directly in the web part settings.
Common use cases
- Filtering content: Show only items with specific metadata values
- Styling content: Apply different styles based on metadata
- Categorizing content: Group or organize content by metadata values
- Adding badges or tags: Display additional visual indicators based on metadata
Advanced example
{{#each content}}
{{#if custom.ShowInWelcome}}
<div class="card {{#if custom.IsHighlighted}}highlighted{{/if}}">
<h3>{{title}}</h3>
{{#if custom.Categories}}
<div class="categories">
{{#each custom.Categories}}
<span class="tag">{{this.label}}</span>
{{/each}}
</div>
{{/if}}
{{#if custom.Expires}}
<div class="expires-on">Expires: {{formatDate custom.Expires "MM/DD/YYYY"}}</div>
{{/if}}
{{#if custom.Owner}}
<div class="owner">
<img src="{{custom.Owner.pictureUrl}}" alt="{{custom.Owner.displayName}}" />
<span>{{custom.Owner.displayName}}</span>
</div>
{{/if}}
</div>
{{/if}}
{{/each}}
Custom variables
The custom
variable is organized by feature and contains user-defined settings:
Path | Description | Example Access |
custom.greeting.* | Variables related to the greeting feature | {{custom.greeting.hide}} |
custom.time.* | Variables related to the time feature | {{custom.time.hide}} |
custom.weather.* | Variables related to the weather feature | {{custom.weather.hide}} |
custom.search.* | Variables related to the search feature | {{custom.search.hide}} |
custom.content.* | Variables related to the content feature | {{custom.content.heading}}, {{custom.content.cycle}} |
custom.background.* | Variables related to the background feature | {{custom.background.title}} |
Audience targeting
To specify which audiences to load, you must list them in a settings metadata section:
<settings>
<content>
<fields>
<field name="WelcomeNewsCategory" />
</fields>
</content>
<!--
List any audiences ids you want to use in your template. These ids then can be used with {{#audience "<guid>"}} helper.
-->
<audiences>
<audience id="54698842-c8c0-4c95-9832-b88a52065ab3" />
</audiences>
</settings>
You can conditionally render content based on audience targeting using Handlebars helpers:
{{#audience "00000000-0000-0000-0000-000000000000"}}
<div>Content for audience 1</div>
{{else}}
{{#audience "11111111-1111-1111-1111-111111111111"}}
<div>Content for audience 2</div>
{{else}}
<div>Default content</div>
{{/audience}}
{{/audience}}
You can also use variables for dynamic audience targeting:
{{#audience custom.content.secret}}
<div>This content is only visible to the specified audience</div>
{{/audience}}
Lifecycle hooks
Three JavaScript functions can be defined to hook into the layout's lifecycle:
1. onInitialize()
Called when the layout is initialized
function onInitialize() {
console.log('Layout initialized');
// Set up resources, event listeners, etc.
var webPart = ctx.getWebPart(); // get web part context
// Example: Make a SharePoint REST API call
webPart.spHttpClient.get(
webPart.pageContext.web.absoluteUrl + '/_api/web?$select=Title',
webPart.spHttpClient.constructor.configurations.v1,
{ headers: { 'Accept': 'application/json' } }
)
.then(function(response) {
return response.json();
})
.then(function(data) {
console.log('Web Title:', data.Title);
})
.catch(function(error) {
console.error('Error:', error);
});
}
2. onError(error)
Called when an error occurs
function onError(error) {
console.error('Layout error:', error);
// Handle errors
}
3. onDispose()
Called when the layout is disposed
function onDispose() {
console.log('Layout disposed');
// Clean up resources, remove event listeners, etc.
clearInterval(interval); // example: clean up interval
}
JavaScript context
A global ctx
object is available in your scripts to interact with the Welcome app:
// Get a custom variable value
var hideTime = ctx.getVariable('custom.time.hide');
// Access the web part context
var webPart = ctx.getWebPart();
// Make SharePoint API calls
webPart.spHttpClient.get(
webPart.pageContext.web.absoluteUrl + '/_api/web',
webPart.spHttpClient.constructor.configurations.v1,
{ headers: { 'Accept': 'application/json' } }
)
.then(response => response.json())
.then(data => console.log('Web Title:', data.Title))
.catch(error => console.error('Error:', error));
Understanding the .SCOPE pseudo-class
The .SCOPE
pseudo-class is a special selector in Welcome app layouts that gets replaced at runtime with the actual scope of the web part instance (e.g., #acc365-Welcome-12345
). This provides several benefits:
- CSS Isolation: Ensures your styles don't affect other elements on the page
- CSS Variables: Define CSS variables that are scoped only to your web part
- External Targeting: Safely target elements outside your layout when necessary
- Prevent Collisions: Avoid CSS class name collisions with other components
When the layout is rendered, .SCOPE
is replaced with the actual web part container selector, turning:
.SCOPE { --primary-color: #0078d4; }
into something like:
#acc365-Welcome-12345 { --primary-color: #0078d4; }
This allows you to safely define styles that won't interfere with other elements on the page, even if they use the same class names.