Template Overrides
Jetonomy’s template system is designed to be overridden without touching plugin files. Place your custom templates in your theme and they will be loaded automatically - no hooks, no filters required for simple overrides.
How It Works
Section titled “How It Works”When Jetonomy loads a template, Template_Loader checks your active theme directory first. If a matching file exists there, it loads that file instead of the plugin’s copy. This means your overrides survive plugin updates.
Resolution order:
{your-theme}/jetonomy/{relative-path}- checked first{jetonomy-plugin}/templates/{relative-path}- fallback default
The {relative-path} is always the same path the plugin uses internally - for example, views/home.php or partials/reply-card.php.
Directory Structure
Section titled “Directory Structure”Create the following folder structure in your active theme:
your-theme/└── jetonomy/ ├── views/ │ ├── home.php │ ├── category.php │ ├── space.php │ ├── space-members.php │ ├── space-roadmap.php │ ├── single-post.php │ ├── new-post.php │ ├── user-profile.php │ ├── edit-profile.php │ ├── leaderboard.php │ ├── notifications.php │ ├── moderation.php │ ├── search.php │ ├── tag.php │ └── invite.php └── partials/ ├── avatar.php ├── breadcrumb.php ├── composer.php ├── header.php ├── pagination.php ├── post-card.php ├── reply-card.php └── sidebar.phpYou do not need to copy all of these. Only create the files you want to customize - any file not present in your theme directory is loaded from the plugin.
Available Templates
Section titled “Available Templates”Views (full-page templates)
Section titled “Views (full-page templates)”| File | Route | URL Pattern |
|---|---|---|
views/home.php |
home |
/community/ |
views/category.php |
category |
/community/category/{slug}/ |
views/space.php |
space |
/community/s/{slug}/ |
views/space-members.php |
space-members |
/community/s/{slug}/members/ |
views/space-roadmap.php |
space-roadmap |
/community/s/{slug}/roadmap/ |
views/space-edit.php |
edit-space |
/community/s/{slug}/edit/ |
views/space-moderation.php |
space-moderation |
/community/s/{slug}/mod/ |
views/single-post.php |
post |
/community/s/{slug}/t/{slug}/ |
views/new-post.php |
new-post |
/community/s/{slug}/new/ |
views/new-space.php |
new-space |
/community/new-space/ |
views/my-spaces.php |
my-spaces |
/community/my-spaces/ |
views/drafts.php |
drafts |
/community/drafts/ |
views/bookmarks.php |
bookmarks |
/community/bookmarks/ |
views/user-profile.php |
profile |
/community/u/{login}/ |
views/edit-profile.php |
edit-profile |
/community/u/{login}/edit/ |
views/leaderboard.php |
leaderboard |
/community/leaderboard/ |
views/notifications.php |
notifications |
/community/notifications/ |
views/moderation.php |
moderation |
/community/mod/ |
views/search.php |
search |
/community/search/ |
views/tag.php |
tag |
/community/tag/{slug}/ |
views/invite.php |
invite |
/community/invite/{code}/ |
Partials (reusable template fragments)
Section titled “Partials (reusable template fragments)”| File | Rendered via |
|---|---|
partials/header.php |
Loaded at the top of every community page |
partials/sidebar.php |
Loaded in space and home views |
partials/breadcrumb.php |
Loaded in space, category, and post views |
partials/post-card.php |
Iterated over in space and home views |
partials/reply-card.php |
Iterated over in single-post view |
partials/pagination.php |
Loaded at the bottom of listing pages |
partials/avatar.php |
Used inside post-card, reply-card, and profile views |
partials/composer.php |
Rich-text composer used in new-post and reply forms |
Step-by-Step: Overriding the Post Card
Section titled “Step-by-Step: Overriding the Post Card”This example adds a “Sponsored” badge to posts from a specific space.
Step 1: Copy the original template
Section titled “Step 1: Copy the original template”Copy wp-content/plugins/jetonomy/templates/partials/post-card.php to:
your-theme/jetonomy/partials/post-card.phpStep 2: Modify your copy
Section titled “Step 2: Modify your copy”Open your-theme/jetonomy/partials/post-card.php and add your customization. The variable $post (a \Jetonomy\Models\Post object) is available inside the partial.
<?php// The $post variable is passed via Template_Loader::partial()$is_sponsored = ( (int) $post->space_id === MY_SPONSORED_SPACE_ID );?>
<div class="jt-row <?php echo $is_sponsored ? 'jt-row--sponsored' : ''; ?>"> <?php if ( $is_sponsored ) : ?> <span class="my-sponsored-badge">Sponsored</span> <?php endif; ?>
<?php // Continue with the original template output. // Tip: call Template_Loader::partial() for nested partials so they also // respect theme overrides. \Jetonomy\Template_Loader::partial( 'avatar', [ 'user_id' => $post->author_id ] ); ?>
<div class="jt-row__body"> <?php // There is no single post-URL helper; build it from base_url + the space and post slugs (same as core templates). ?> <a href="<?php echo esc_url( \Jetonomy\base_url() . '/s/' . ( $space->slug ?? '' ) . '/t/' . $post->slug . '/' ); ?>"> <?php echo esc_html( $post->title ); ?> </a> </div></div>Step 3: Add styles
Section titled “Step 3: Add styles”Add CSS to your theme for the new .jt-row--sponsored class. Reference Jetonomy’s CSS tokens so your styles adapt to dark mode automatically:
/* your-theme/style.css or an enqueued stylesheet */
.jt-row--sponsored { border-left: 3px solid var(--jt-accent);}
.my-sponsored-badge { display: inline-block; font-size: 0.75rem; color: var(--jt-text-secondary); background: var(--jt-accent-light); border-radius: var(--jt-radius-sm); padding: 2px 6px;}Calling Partials from Templates
Section titled “Calling Partials from Templates”Inside any view or partial, use Template_Loader::partial() instead of a raw include. This ensures the theme-override check runs for nested partials too:
// Inside your custom view or partial:\Jetonomy\Template_Loader::partial( 'pagination', [ 'total' => $total, 'per_page' => 20, 'page' => $current_page,] );The second argument is passed as local variables to the partial (via extract()).
Available Variables in Templates
Section titled “Available Variables in Templates”Templates receive route data via $data (array with route and slug keys). Most views also query the database directly at the top of the file. When you copy a template, review the top of the plugin’s original file to understand which variables are set before the HTML output begins.
Common objects available in plugin templates:
| Variable | Type | Available in |
|---|---|---|
$data |
array |
All views |
$space |
\Jetonomy\Models\Space |
space, space-members, new-post |
$post |
\Jetonomy\Models\Post |
single-post, post-card partial |
$reply |
\Jetonomy\Models\Reply |
reply-card partial |
$user |
WP_User |
user-profile, edit-profile |
$posts |
array |
home, space, search, tag |
$settings |
array |
All views (from get_option('jetonomy_settings')) |
The jetonomy_template_map Filter
Section titled “The jetonomy_template_map Filter”For cases where you need to register a completely new route - or override the template for an existing route with a file stored outside the theme - use the jetonomy_template_map filter.
add_filter( 'jetonomy_template_map', function( array $map ): array { // Override an existing route with an absolute path. $map['leaderboard'] = get_stylesheet_directory() . '/jetonomy/views/leaderboard.php';
// Register a brand-new route (accessible at /community/events/). $map['events'] = MY_PLUGIN_DIR . 'templates/community-events.php';
return $map;} );Important: If you provide an absolute path (starting with /), the theme-override check is bypassed - the file you point to is loaded directly. This is the correct approach for Pro extensions and companion plugins that ship their own templates.
The Jetonomy Router must know about new routes before they can receive traffic. Register rewrite rules alongside the template map:
// Register a custom rewrite rule for /community/events/.add_action( 'init', function() { $settings = get_option( 'jetonomy_settings', [] ); $base_slug = $settings['base_slug'] ?? 'community';
add_rewrite_rule( '^' . preg_quote( $base_slug, '^' ) . '/events/?$', 'index.php?jetonomy_route=events', 'top' );} );
// Teach WordPress to recognize the query var.add_filter( 'query_vars', function( array $vars ): array { $vars[] = 'jetonomy_route'; return $vars;} );After adding rewrite rules, flush permalinks once: go to Settings → Permalinks and click Save Changes, or run:
wp --path="/path/to/wordpress" rewrite flushChild Theme Compatibility
Section titled “Child Theme Compatibility”If you are using a child theme, place overrides in the child theme directory - get_stylesheet_directory() resolves to the child theme path when a child theme is active. The plugin does not check the parent theme separately, so all overrides must live in the active (child) theme.
Pro Template Overrides
Section titled “Pro Template Overrides”Jetonomy Pro registers its own templates for Private Messaging via the jetonomy_template_map filter using absolute paths. You can override Pro templates in your theme using the same directory structure:
your-theme/└── jetonomy/ └── views/ ├── messages.php # /community/messages/ └── conversation.php # /community/messages/{id}/Place overrides here and they will be detected automatically because Pro’s Template_Loader::partial() calls still respect the theme directory check for non-absolute paths.
What’s Next?
Section titled “What’s Next?”- Hooks Reference - Inject content at specific points without overriding full templates
- REST API Reference - Fetch data to power your custom templates
- Shortcodes, Widgets & Blocks - Embed community content on non-community pages

