Shortcodes

Shortcodes let you add dynamic content to your Markdown without writing raw HTML. They're simple tags in square brackets that get replaced when the page renders.

How They Work

Shortcodes come in two forms:

<!-- Self-closing -->
Copyright © [​year] [​site_name]

<!-- Paired (with content between) -->
[​email][email protected][/​email]

When rendered, [​year] becomes the current year, [​site_name] becomes your site name, and [​email]...[/​email] creates a spam-protected mailto link.

Built-in Shortcodes

Shortcode Output
[​year] Current year
[​date format="Y-m-d"] Current date (optionally formatted)
[​site_name] Site name from config
[​site_url] Site URL from config
[​email][email protected][/​email] Obfuscated mailto link
[​snippet name="file"] Renders app/snippets/file.php

Creating Custom Shortcodes

Register shortcodes in your theme.php:

<?php
// app/themes/yourtheme/theme.php

use Ava\Application;

return function (Application $app): void {
    $shortcodes = $app->shortcodes();

    // Self-closing shortcode
    $shortcodes->register('greeting', function (array $attrs, ?string $content, string $tag) {
        $name = $attrs['name'] ?? 'friend';
        return "Hello, " . htmlspecialchars($name) . "!";
    });

    // Paired shortcode (receives content)
    $shortcodes->register('highlight', function (array $attrs, ?string $content, string $tag) {
        $color = $attrs['color'] ?? 'yellow';
        $inner = $content ?? '';
        return '<mark style="background:' . htmlspecialchars($color) . '">' . $inner . '</mark>';
    });
};

Usage:

[​greeting name="Alice"]

[​highlight color="#ffeeba"]This text is highlighted.[/​highlight]
Heads up: Ava CMS processes shortcodes after Markdown, so shortcodes can run even inside code blocks/tables. This page inserts an invisible character after [ in examples so they display correctly without executing.

Shortcode Callback Parameters

Parameter Type Description
$attrs array All attributes passed to the shortcode
$content ?string Content between opening/closing tags (null when not provided)
$tag string Normalized shortcode tag name (lowercase)

If your callback only needs $attrs, you can still declare fewer parameters — PHP will ignore the extra arguments Ava CMS passes.

Attributes

Attribute parsing supports:

  • key="value"
  • key='value'
  • key=value
  • Boolean flags: key (sets $attrs['key'] = true)

Best Practices

  • Escape output — Always use htmlspecialchars() for user-provided values to prevent XSS
  • Return strings — Return a string (or null, which becomes an empty string)
  • Keep it simple — Complex shortcodes are better as snippets (see below)
  • Name carefully — Shortcode names are case-insensitive, use underscores for multi-word names

Snippets: Reusable PHP Components

For more complex components, use snippets. A snippet is a PHP file in your app/snippets/ folder that you invoke with the [​snippet] shortcode.

When to use snippets vs shortcodes:

Use Shortcodes Use Snippets
Simple text replacements Complex HTML structures
No external files needed Reusable across sites
1-5 lines of code Need full PHP file with logic
Site-specific utilities Component libraries

Creating a Snippet

<?php // app/snippets/cta.php ?>
<?php
$heading = $params['heading'] ?? 'Ready to get started?';
$button = $params['button'] ?? 'Learn More';
$url = $params['url'] ?? '/contact';
?>
<div class="cta-box">
    <h3><?= htmlspecialchars($heading) ?></h3>
    <p><?= $content ?></p>
    <a href="<?= htmlspecialchars($url) ?>" class="button">
        <?= htmlspecialchars($button) ?>
    </a>
</div>

Using a Snippet

[​snippet name="cta" heading="Join Our Newsletter" button="Subscribe" url="/subscribe"]
Get weekly tips delivered to your inbox.
[/​snippet]

Variables in Snippets

Variable Description
$content Text between opening/closing tags
$params Array of all attributes (e.g., $params['heading'])
$ava Rendering engine instance (Ava\Rendering\Engine)
$app Application instance

Example Snippets

YouTube Embed:

<?php // app/snippets/youtube.php ?>
<?php $id = $params['id'] ?? ''; ?>
<div class="video-embed" style="aspect-ratio: 16/9;">
    <iframe src="https://www.youtube.com/embed/<?= htmlspecialchars($id) ?>" 
            frameborder="0" allowfullscreen style="width:100%;height:100%;"></iframe>
</div>

Usage: [​snippet name="youtube" id="dQw4w9WgXcQ"]

Notice Box:

<?php // app/snippets/notice.php ?>
<?php
$type = $params['type'] ?? 'info';
$icons = ['info' => '💡', 'warning' => '⚠️', 'success' => '✅', 'error' => '❌'];
$icon = $icons[$type] ?? '💡';
?>
<div class="notice notice-<?= htmlspecialchars($type) ?>">
    <span><?= $icon ?></span>
    <div><?= $content ?></div>
</div>

Usage:

[​snippet name="notice" type="warning"]
This feature is experimental.
[/​snippet]

Pricing Card:

<?php // app/snippets/pricing.php ?>
<?php
$plan = $params['plan'] ?? 'Plan';
$price = $params['price'] ?? '$0';
$features = $params['features'] ?? '';
$url = $params['url'] ?? '#';
?>
<div class="pricing-card">
    <h3><?= htmlspecialchars($plan) ?></h3>
    <div class="price"><?= htmlspecialchars($price) ?><span>/month</span></div>
    <ul>
        <?php foreach (explode(',', $features) as $feature): ?>
            <li><?= htmlspecialchars(trim($feature)) ?></li>
        <?php endforeach; ?>
    </ul>
    <a href="<?= htmlspecialchars($url) ?>" class="button">Get Started</a>
</div>

Usage: [​snippet name="pricing" plan="Pro" price="$29" features="Unlimited projects, Priority support, API access"]

How Processing Works

  1. Markdown is converted to HTML
  2. Shortcodes are processed in the HTML output
  3. Path aliases are expanded
  4. Result is sent to the browser

Since shortcodes run after Markdown processing, they can safely output raw HTML.

Limitations

  • No nested shortcodes — shortcodes are not processed inside other shortcodes.
  • Paired content cannot contain [ — the current parser stops paired shortcode content at the next [ character. If you need rich/nested markup, prefer a snippet.

Security

  • Path safety: Snippet names can't contain .. or / — no directory traversal
  • Disable snippets: Set security.shortcodes.allow_php_snippets to false
  • Unknown shortcodes: Left as-is in output (no errors)
  • Errors: Exceptions are logged and replaced with an HTML comment
  • Escaping: Always use htmlspecialchars() for user values