Plugin Development
Shortcodes

Shortcodes

Create embeddable content with simple tags.

What are Shortcodes?

Shortcodes let users add dynamic content to posts using simple brackets:

[button text="Click me" url="/signup"]

[gallery columns="3"]

[alert type="warning"]Be careful![/alert]

Registering Shortcodes

Basic Shortcode

sdk.shortcodes.register('year', () => {
  return new Date().getFullYear().toString();
});
 
// Usage: The year is [year]
// Output: The year is 2024

With Attributes

sdk.shortcodes.register('button', (attrs) => {
  const {
    text = 'Click',
    url = '#',
    color = 'primary'
  } = attrs;
  
  return `<a href="${url}" class="btn btn-${color}">${text}</a>`;
});
 
// Usage: [button text="Sign Up" url="/signup" color="green"]

With Content

sdk.shortcodes.register('alert', (attrs, content) => {
  const { type = 'info' } = attrs;
  
  const icons = {
    info: 'ℹ️',
    warning: '⚠️',
    success: '✅',
    error: '❌'
  };
  
  return `
    <div class="alert alert-${type}">
      <span class="alert-icon">${icons[type] || ''}</span>
      <div class="alert-content">${content}</div>
    </div>
  `;
});
 
// Usage: [alert type="warning"]Please read this![/alert]

Async Shortcodes

For database queries or API calls:

sdk.shortcodes.register('latest-posts', async (attrs) => {
  const { count = 5, category = '' } = attrs;
  
  const filter = category ? { category } : {};
  
  const posts = await sdk.content.getPosts({
    filter,
    limit: parseInt(count),
    sort: { createdAt: -1 }
  });
  
  let html = '<ul class="latest-posts">';
  posts.items.forEach(post => {
    html += `<li><a href="${post.url}">${post.title}</a></li>`;
  });
  html += '</ul>';
  
  return html;
});
 
// Usage: [latest-posts count="10" category="news"]

Context Access

Access page context in shortcodes:

sdk.shortcodes.register('current-category', (attrs, content, context) => {
  // context.post - current post (if on post page)
  // context.category - current category (if on category page)
  // context.user - logged in user
  // context.request - request object
  
  if (context.category) {
    return context.category.name;
  }
  
  if (context.post && context.post.category) {
    return context.post.category.name;
  }
  
  return 'Uncategorized';
});

Attribute Types

Attributes are always strings. Convert as needed:

sdk.shortcodes.register('grid', (attrs) => {
  // Convert to number
  const columns = parseInt(attrs.columns) || 3;
  
  // Convert to boolean
  const showBorder = attrs.border === 'true';
  
  // Handle arrays (comma-separated)
  const items = (attrs.items || '').split(',').map(s => s.trim());
  
  return `<div class="grid grid-${columns}" ${showBorder ? 'style="border: 1px solid #ccc"' : ''}>
    ${items.map(item => `<div>${item}</div>`).join('')}
  </div>`;
});
 
// Usage: [grid columns="4" border="true" items="one,two,three,four"]

Common Shortcode Examples

YouTube Embed

sdk.shortcodes.register('youtube', (attrs) => {
  const { id, width = '560', height = '315' } = attrs;
  
  if (!id) return '<p class="error">YouTube ID required</p>';
  
  return `
    <div class="video-container">
      <iframe 
        width="${width}" 
        height="${height}"
        src="https://www.youtube.com/embed/${id}"
        frameborder="0"
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen>
      </iframe>
    </div>
  `;
});
 
// Usage: [youtube id="dQw4w9WgXcQ"]

Accordion/Collapsible

sdk.shortcodes.register('accordion', (attrs, content) => {
  const { title = 'Click to expand' } = attrs;
  const id = 'acc-' + Math.random().toString(36).substr(2, 9);
  
  return `
    <div class="accordion">
      <button class="accordion-toggle" aria-expanded="false" aria-controls="${id}">
        ${title}
        <span class="accordion-icon">▼</span>
      </button>
      <div id="${id}" class="accordion-content" hidden>
        ${content}
      </div>
    </div>
  `;
});
 
// Usage: [accordion title="Read more"]Hidden content here[/accordion]

Tabs

// Store tabs temporarily
let currentTabs = [];
 
sdk.shortcodes.register('tabs', (attrs, content) => {
  const tabs = [...currentTabs];
  currentTabs = []; // Reset for next use
  
  if (tabs.length === 0) return '';
  
  const id = 'tabs-' + Math.random().toString(36).substr(2, 9);
  
  let html = `<div class="tabs" id="${id}">`;
  
  // Tab buttons
  html += '<div class="tab-buttons">';
  tabs.forEach((tab, i) => {
    html += `<button class="tab-btn ${i === 0 ? 'active' : ''}" data-tab="${i}">${tab.title}</button>`;
  });
  html += '</div>';
  
  // Tab panels
  html += '<div class="tab-panels">';
  tabs.forEach((tab, i) => {
    html += `<div class="tab-panel ${i === 0 ? 'active' : ''}" data-panel="${i}">${tab.content}</div>`;
  });
  html += '</div></div>';
  
  return html;
});
 
sdk.shortcodes.register('tab', (attrs, content) => {
  currentTabs.push({
    title: attrs.title || 'Tab',
    content: content
  });
  return ''; // Content handled by parent
});
 
// Usage:
// [tabs]
//   [tab title="First"]Content 1[/tab]
//   [tab title="Second"]Content 2[/tab]
// [/tabs]

Pricing Table

sdk.shortcodes.register('pricing', (attrs) => {
  const {
    name = 'Plan',
    price = '$0',
    period = 'month',
    features = '',
    cta = 'Sign Up',
    url = '#',
    featured = 'false'
  } = attrs;
  
  const featureList = features.split(',').map(f => f.trim());
  const isFeatured = featured === 'true';
  
  return `
    <div class="pricing-card ${isFeatured ? 'pricing-featured' : ''}">
      ${isFeatured ? '<span class="pricing-badge">Popular</span>' : ''}
      <h3 class="pricing-name">${name}</h3>
      <div class="pricing-price">
        <span class="price">${price}</span>
        <span class="period">/${period}</span>
      </div>
      <ul class="pricing-features">
        ${featureList.map(f => `<li>✓ ${f}</li>`).join('')}
      </ul>
      <a href="${url}" class="pricing-cta">${cta}</a>
    </div>
  `;
});
 
// Usage: [pricing name="Pro" price="$29" features="Feature 1,Feature 2,Feature 3" featured="true"]

Code Block

sdk.shortcodes.register('code', (attrs, content) => {
  const { language = 'text', filename = '' } = attrs;
  
  // Escape HTML
  const escaped = content
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
  
  return `
    <div class="code-block">
      ${filename ? `<div class="code-filename">${filename}</div>` : ''}
      <pre><code class="language-${language}">${escaped}</code></pre>
    </div>
  `;
});
 
// Usage: [code language="javascript" filename="app.js"]const x = 1;[/code]

Testimonial

sdk.shortcodes.register('testimonial', (attrs, content) => {
  const { name = '', title = '', image = '' } = attrs;
  
  return `
    <blockquote class="testimonial">
      <p class="testimonial-content">"${content}"</p>
      <footer class="testimonial-author">
        ${image ? `<img src="${image}" alt="${name}" class="testimonial-image">` : ''}
        <div class="testimonial-info">
          <cite class="testimonial-name">${name}</cite>
          ${title ? `<span class="testimonial-title">${title}</span>` : ''}
        </div>
      </footer>
    </blockquote>
  `;
});
 
// Usage: [testimonial name="John Doe" title="CEO, Company" image="/avatar.jpg"]Great product![/testimonial]

Removing Shortcodes

sdk.shortcodes.unregister('oldShortcode');

Check If Exists

if (sdk.shortcodes.exists('button')) {
  console.log('Button shortcode is registered');
}

Adding Styles

Include CSS for your shortcodes:

sdk.hooks.addAction('theme_head', () => {
  return `
    <style>
      .alert {
        padding: 15px;
        border-radius: 4px;
        margin: 15px 0;
        display: flex;
        gap: 10px;
      }
      .alert-info { background: #e3f2fd; border-left: 4px solid #2196f3; }
      .alert-warning { background: #fff3e0; border-left: 4px solid #ff9800; }
      .alert-success { background: #e8f5e9; border-left: 4px solid #4caf50; }
      .alert-error { background: #ffebee; border-left: 4px solid #f44336; }
    </style>
  `;
});