Tutorial: Using the Template Builder
The Template Builder is a full-screen visual editor for creating reusable post-card templates and titlebars. Templates control how each post card looks when rendered by the Posts widget in the Page Builder — the image, title, excerpt, author, date, and category label arrangement.
This tutorial walks through creating a template from scratch, configuring nodes, working with the responsive preview, and saving. By the end you will have a working template that can be selected in any Posts widget.
Before You Start
What a template is (and is not)
A template is layout only. It defines how a single post card looks — the arrangement of visual elements. It does not:
- Fetch posts or run WP_Query
- Know how many posts to show (the Posts widget controls that)
- Handle filtering, pagination, or category selection (also the Posts widget)
The Template Builder is intentionally isolated from data concerns. Its output is a JSON node tree that the PHP compiler converts into a .php file at wp-content/sanilwb-templates/template-{id}.php. That compiled file is what the Posts widget actually includes when rendering.
Template types
| Builder type | What it builds | Used by |
|---|---|---|
template |
Post-card layout (image, title, excerpt, date, etc.) | Posts widget in the Page Builder |
titlebar |
Title bar shown above a carousel section | Title Bar widget in the Page Builder |
Both types share the same editor UI. This tutorial covers the template type. The titlebar type works identically but some node types and fields are only available in one or the other.
Accessing the Template List
Go to WordPress Admin → Templates (in the Sanil Website Builder menu group). The list page shows all existing templates in a WP_List_Table with Name, Category, and Last Modified columns.
Actions per template:
| Action | What it does |
|---|---|
| Edit | Opens the template in the editor |
| Trash | Moves to trash (recoverable) |
| Duplicate | Creates a copy with the same node tree and a "Copy of …" name |
Trashed templates can be Restored or Permanently Deleted from the Trash view.
Creating a New Template
Click Add New at the top of the template list. The editor opens with an empty canvas and an auto-generated name ("Template 3", "Template 4", etc. based on the current count).
Understanding the Editor UI
The editor has three areas:
| Area | Location | Purpose |
|---|---|---|
| Topbar | Top bar | Template name input, settings dropdown, Save button |
| Node Tree | Left sidebar | Hierarchical list of all nodes — add, reorder, configure, copy/paste styles, delete |
| Canvas | Right side | Live iframe preview of the current node tree using sample post data |
Below the canvas, Device Preview Controls (Desktop / Tablet / Mobile buttons) switch the canvas width to simulate different viewports.
The Settings dropdown
Click the gear icon in the topbar to open the Settings dropdown. It contains:
- Category — a select that assigns this template to a category. Categories group templates in the Posts widget picker.
- Preview Post Picker — a search input that lets you pick a real post from your site to use as the canvas preview data. By default the canvas shows Nepali placeholder text — searching for and selecting a real post gives you a much more realistic preview.
The Node Tree
The Node Tree is the left sidebar. It shows all nodes in the template as a hierarchical list. Every operation happens here:
| Button | Action |
|---|---|
| + (Add Node) | Opens the Add Node menu to insert a new node |
| Edit (pencil icon) | Opens the settings dialog for that node |
| Copy Styles | Copies all style-tab field values from the node to the clipboard |
| Paste Styles | Applies the clipboard styles to the node (only active when clipboard has matching type) |
| Delete (trash icon) | Removes the node and all its descendants |
div nodes (containers) show a child count indicator and a collapse toggle so you can fold nested structures.
Nodes can be reordered by dragging within the same parent. You cannot reparent a node via drag (e.g. drag a heading out of one div into another div) — use delete and re-add for that.
Step 1 — Add the Root Container
Templates must have a top-level div container. Click + (Add Node) to open the Add Node menu and select Div (Container).
The div is inserted as the first node in the tree. It immediately appears in the canvas as an empty container.
Why start with a div?
The div is the only node type that can have children. Every other node type (heading, image, excerpt, etc.) is a leaf and holds no children. Your template's entire structure lives inside one or more root-level divs.
Step 2 — Configure the Root Div
Click the Edit (pencil) button on the div node to open its settings dialog.
The div has a Style tab with layout controls:
| Field | What it controls |
|---|---|
| Direction | row (side by side) or column (stacked) |
| Align Items | Flex cross-axis alignment (start, center, end, stretch) |
| Justify Content | Flex main-axis distribution (start, center, end, space-between, etc.) |
| Flex Wrap | Whether children wrap to the next line |
| Gap | Space between children |
| Background Color | The div's background fill |
| Min/Max Width | Constrain the div's width |
| Min/Max Height | Constrain the div's height |
| Box Shadow | One of several preset shadow depths |
For a standard vertical card layout (image → title → excerpt), set Direction to column. Click Save.
Step 3 — Add Child Nodes
With the root div selected in the node tree (click its row to select it), click + (Add Node). When a container div is selected, new nodes are inserted as children inside it. When a leaf node is selected, new nodes are inserted as siblings after it.
Add these nodes in order:
- Image — the post's featured image
- Heading — the post title
- Excerpt — the post excerpt or summary
After adding each node, it appears indented under the root div in the tree and immediately renders in the canvas using sample data.
Available node types
| Type | Builder | canHaveChildren |
Renders |
|---|---|---|---|
| Div (Container) | template, titlebar | Yes | A flex/block wrapper |
| Image | template only | No | The post's featured image |
| Heading | template, titlebar | No | The post title (or custom text in titlebar mode) |
| Excerpt | template only | No | The post excerpt |
| Date | template only | No | The post date (time-ago or formatted) |
| Author | template only | No | Author name with optional avatar |
| Button | template, titlebar | No | A styled button or link |
| Topic | template only | No | The post's primary category with optional link |
| Carousel Nav | titlebar only | No | Previous / Next carousel navigation buttons |
Step 4 — Configure the Image Node
Click the Edit button on the Image node.
| Field | What it controls |
|---|---|
| Image Size | Which WordPress registered image size to use (thumbnail, medium, large, full, etc.) |
| Link | Wrap the image in a link to the post |
| Width | Custom width (px or %) |
| Height | Custom height |
| Animate on Hover | Adds a subtle scale effect when hovering |
| Box Shadow | Preset shadow depth |
For a standard card, set Image Size to medium and Link to on. Click Save.
Step 5 — Configure the Heading Node
Click the Edit button on the Heading node.
| Field | What it controls |
|---|---|
| Link | Wrap the heading in a link to the post |
| Text Color | Heading color |
| Hover Color | Color when the cursor hovers over the linked heading |
| Font Size | Font size in px (responsive) |
| Font Family | Google Fonts picker |
| Font Weight | Weight dropdown, filtered to available weights for the selected font |
| Font Style | Normal or Italic |
Set Link to on and choose a Text Color. For dark backgrounds, always set a Hover Color too — without it the default browser hover style (usually the same color) applies and the hover state is invisible against dark backgrounds.
Click Save. The canvas updates immediately to reflect the new font/color.
Step 6 — Configure the Excerpt Node
Click the Edit button on the Excerpt node.
| Field | What it controls |
|---|---|
| Lines | Line clamp: limits the excerpt to 2–5 lines with a … truncation |
| Text Color | Excerpt paragraph color |
| Font Size, Family, Weight, Style | Typography controls |
| Text Align | Left, center, right |
Set Lines to 3 to prevent long excerpts from breaking the card layout. Click Save.
Step 7 — Add an Author and Date Row (Optional)
To build a footer row inside the card (e.g. author name on the left, date on the right), you add a second child div configured as a flex row:
- Select the root div node (click its row in the tree).
- Click + → Div (Container). A new div is added as a child.
- Open its settings. Set Direction to
rowand Justify Content tospace-between. - Select the new div and click + → Author.
- Select the new div again and click + → Date.
The canvas should now show the author and date side by side at the bottom of the card.
Step 8 — Use the Responsive Preview
Switch between Desktop, Tablet, and Mobile using the device buttons below the canvas.
| Mode | Canvas iframe width |
|---|---|
| Desktop | Unconstrained |
| Tablet | 768 px |
| Mobile | 375 px |
When you open a node's settings dialog, the active device controls which breakpoint's values are being edited. For fields marked responsive: true, you can set:
- One value for Desktop (base)
- A different value for Tablet (override)
- A different value for Mobile (override)
The cascade works as: Mobile → falls back to Tablet → falls back to Desktop if no override is set.
Example: You want the heading font size to be 20px on desktop, 18px on tablet, and 16px on mobile.
- Switch to Desktop in the device controls.
- Open the Heading node settings. Set Font Size to
20. Save. - Switch to Tablet.
- Open Heading settings again. Set Font Size to
18. Save. - Switch to Mobile.
- Open Heading settings again. Set Font Size to
16. Save.
The canvas updates for each device. On the frontend, the PHP compiler emits matching @media queries.
Step 9 — Use the Preview Post Picker
By default the canvas shows Nepali placeholder text. To see how your template looks with real post content:
- Click the gear icon in the topbar to open the Settings dropdown.
- In the Preview Post search input, type the name of a post on your site.
- Select the post from the results.
- The canvas updates to use that post's title, excerpt, author name, avatar, date, and category.
The preview post selection is per-session — it resets when you reload the page. It does not affect the saved template.
Step 10 — Copy and Paste Node Styles
If you have two nodes of the same type (e.g. two button nodes) and want them to share the same styling:
- Configure the first node completely.
- Click the Copy Styles button on its row in the node tree.
- Click the Paste Styles button on the second node's row. Paste Styles only activates when the clipboard holds styles from a matching node type — it will not paste heading styles onto an image node.
Pasted styles replace all style-tab field values on the target node. Content-tab values (heading link, image size, excerpt lines, etc.) are not affected.
Step 11 — Name and Save the Template
Before saving, update the template name. Click the name input in the topbar (it shows the auto-generated name like "Template 5") and type a descriptive name like "Standard Card" or "Full-Width Hero".
Click Save in the topbar. The button shows:
| State | Meaning |
|---|---|
| Greyed out | No unsaved changes |
| Active | Unsaved changes exist |
| "Saving…" | Request in flight |
| "Saved ✓" | Success (1.5 seconds, then returns to active/idle) |
What happens on save:
- The JS serializes the node tree to a JSON string (
buildSavePayload()). - An AJAX POST is sent to
sanilwb_template_save. - The handler upserts the row in
wp_sanilwb_templates. SANILWB_Template_Compiler::compile()runs and writes the compiled.phpand.cssfiles towp-content/sanilwb-templates/.- Any Google Fonts used in the template are queued for download.
- The handler returns the template's DB id. If this was a new template, the store updates its
templateIdso subsequent saves use the correct id.
Step 12 — Use the Template in the Page Builder
After saving:
- Open any page in the Page Builder.
- Add or select an existing Posts widget.
- Open its settings dialog → Template tab.
- Your new template appears in the list by the name you gave it.
- Select it. The Content tab unlocks and shows display toggles that match the node types in your template (Show Image, Show Excerpt, etc.).
- Save the widget and check the canvas — the posts should render using your new card layout.
Common Mistakes
I added a node but it didn't go inside the div I expected
Node insertion depends on what is currently selected: - If a div is selected → new node goes inside that div as its last child. - If a leaf node is selected → new node goes after that leaf as a sibling. - If nothing is selected → new node goes at the root level.
Click the div row to select it before clicking + if you want to add children to it.
The canvas shows nothing after adding nodes
Open the browser DevTools console and look for JavaScript errors. Also confirm npm run start is running — if the JS bundle was not rebuilt after your code changes, the canvas may be running stale code.
The compiled template doesn't match the canvas preview
The canvas preview is generated by buildNodeStyles.js (JS-side CSS) and renderCanvas.js (JS-side HTML). The compiled template is generated by class-sanilwb-template-compiler.php. If they diverge, a node type's CSS or HTML logic is out of sync between the PHP compiler and the JS preview. See the Create a New Node Type tutorial for details.
A responsive font size isn't applying on the frontend
Check that the field is marked responsive: true in nodeTypes.js. If it is responsive: false, only one value is stored and no breakpoint overrides are saved. The fix: change it to responsive: true (this is safe for existing saved templates — they will simply have no tablet/mobile overrides and fall back to the desktop base value).
The Save button stays greyed out
The template has no unsaved changes. If you believe changes were made, check the browser console for errors — a failed save attempt may have left the store in a state where isDirty is false even though the last save failed.