Skip to content
Developer docs

Theme reference: variables and helpers

5 min read

A quick-lookup page for theme authors. For the concepts, start with Theme development.

Variables every template gets

VariableContents
$baseURL prefix of the install: '' at a domain root, /blog in a subfolder
$site_title, $taglineFrom the Customizer (Site identity)
$logo_url, $favicon_urlImage URLs, or empty
$footer_textFooter text from the Customizer, or empty
$primary_menu, $footer_menuMenu trees for the primary and footer locations, ready for menu_html()
$seoThis page's title, description and so on, for showing (a breadcrumb, a share link). Core prints the tags itself
$current_urlThe request path
$custom_cssThe site's custom CSS (already printed by bh_head())
$is_hometrue on the front page
$is_previewtrue when an editor is previewing a draft

Per template

TemplateVariables
index.php$posts (rows for this page), $meta (pagination)
single.php$post, $terms (its categories and tags), $comments (approved), $comments_count, $comments_open
page.php$post
archive.php$posts, $meta, $archive_type (category, tag or author), $term (for categories and tags), $author (for authors), $title
search.php$posts, $meta, $query

Note: On single.php and page.php, $page holds the same row as $post, not a page number. Read pagination from $meta['page'].

When the front page is set to a static page, core renders page.php for it with $is_home = true.

$meta (pagination)

['page' => 2, 'per_page' => 10, 'total' => 57, 'last_page' => 6]

A post row ($post, items of $posts)

FieldNotes
id, uuid, slug, typetype is post or page
title, excerptPlain text; escape when printing
contentFinished HTML on single and page templates. Print as is
content_formatblocks, markdown or html: how it was written, not how it arrives
statuspublished, or draft/pending in a preview
published_at, created_at, updated_atUTC, Y-m-d H:i:s. Show with bh_date()
author_id, author_name, author_avatar_urlThe author
featured_url, featured_alt, featured_width, featured_height, featured_sizesFeatured image; use bh_post_image()
comment_statusopen or closed
view_countViews counted by core

A term row ($terms, $term)

id, name, slug, description, parent_id, count, and on $terms also taxonomy_slug (category or tag). Link a term with:

<?php $prefix = $t['taxonomy_slug'] === 'tag' ? '/tag/' : '/category/'; ?>
<a href="<?= htmlspecialchars(link_to($prefix . $t['slug'])) ?>"><?= htmlspecialchars($t['name']) ?></a>

A comment row ($comments)

id, parent_id (empty for a top-level comment), author_name, author_url, content (plain text: escape it), created_at (UTC).

Page structure

HelperUse
bh_head()Echo inside <head>. Required
bh_footer()Echo before </body>. Required
bh_body_class($extra)Classes for <body>: is-home or is-inner, the theme slug, preview state, plus $extra
bh_ajax_client()Load Basehim.ajax on the public site. Call before bh_head()
bh_is_preview()true inside the Customizer's preview frame
HelperReturns
link_to($path)$path with the install's base path, e.g. link_to('/search')
\App\Core\Helpers::postUrl($post)A post's or page's path under the current permalink setting (prefix with $base)
theme_asset($file, $version = true)URL of a file in this theme's assets/, with ?v=
icon($name, $class)A Heroicon as inline SVG, e.g. icon('magnifying-glass', 'w-4 h-4')
HelperUse
menu_html($items, $opts)Nested <ul> with dropdowns to three levels. $opts: class (default bh-menu; keep bh-menu in it for the built-in styles), aria, depth
menu_at($location)Items for any location declared in theme.json
menu_has_children($items)Whether any item has children
menu_assets()Dropdown CSS and behaviour; echo once in <head>
<?= menu_html($primary_menu, ['class' => 'bh-menu site-nav', 'aria' => 'Primary']) ?>
<?= menu_html(menu_at('utility')) ?>

Widgets

HelperUse
widget_area($key)Rendered widgets of an area, or ''
has_widget_area($key)Whether the area has any widgets
<?php if (has_widget_area('sidebar')): ?>
    <aside><?= widget_area('sidebar') ?></aside>
<?php endif; ?>

Comments

HelperUse
bh_comment_form($post, $args)Core's comment form: CSRF token, honeypot, replies, no page reload. Returns the "closed" text when comments are off
bh_comment_count($post)Approved comments on a post

Mark a count so the form updates it after a new comment:

<span data-bh-comment-count="<?= (int) $post['id'] ?>"><?= (int) $comments_count ?></span>

Any element with data-bh-reply data-id="…" data-name="…" turns the form into a reply to that comment. Style hooks and options (class, button_class, label_submit, styles => false…) are listed in Customizer, menus and widgets.

Authors

HelperReturns
bh_author_box($post, $args)The author box (avatar, name, bio, link). Empty when Settings → Reading → Author box on posts is off
bh_author($post)Public profile: display_name, bio, slug, url, avatar_url, post_count
bh_author_url($post)Path to the author's archive, or '' when archives are off or the author has no posts
bh_avatar($userOrPost, $size)An avatar <img>, or initials

Images

HelperReturns
bh_post_image($post, $size, $attrs)Responsive <img> of the featured image (srcset, width, height, lazy loading), or ''
bh_image_url($post, $size)One size's URL; falls back to the original
bh_image_sizes($post)All sizes, smallest first: name => [url, width, height]

Sizes: thumbnail (square), medium, large, full (original).

Dates and times

Stored times are UTC. These show them in the site's timezone and formats:

HelperExample output
bh_date($t, $format = null)October 10, 2026
bh_time($t, $format = null)1:32 pm
bh_datetime($t, $format = null)October 10, 2026 1:32 pm
bh_time_ago($t, $maxDays = 7)5 minutes ago
bh_time_tag($t, $format = null, $relative = false)<time datetime="2026-10-10T13:32:00+05:00">…</time>
bh_iso8601($t)2026-10-10T13:32:00+05:00
bh_local_time($t)DateTimeImmutable in the site timezone
bh_to_utc($local)A time typed in the site timezone, as UTC to store
bh_now()Now, UTC, for storing
bh_timezone()e.g. Asia/Karachi

Settings and options

HelperReturns
bh_theme_option($key, $default)A Customizer option of this theme, preview-aware
bh_setting($group, $key, $default)A site setting, e.g. bh_setting('reading', 'posts_per_page', 10)

SEO

HelperUse
bh_seo()This page's SEO head: set(), get(), addJsonLd(), disable(), handOff()
bh_seo_value($key)Read one value back, e.g. bh_seo_value('title')

See SEO, dates and images.

AI agents (WebMCP)

HelperUse
bh_webmcp_form($name, $description, $opts)Attributes that describe a form to in-browser agents
bh_webmcp_param($description)Describes one form field

Both return '' while WebMCP is off, so they are safe to leave in. See AI discovery.

Discussion

Leave a comment