Ik zal je specifiek laten zien hoe ik shortcodes direct in het thema registreer, ze veilig aanlever en ze op de juiste plaatsen render - zo bouw ik een wordpress shortcode thema met een duidelijke structuur, schone code en snelle laadtijden. Ik geef praktische voorbeelden, leg do_shortcode uit in het sjabloon, bespreek attributen, escaping en caching en voeg best practices toe voor onderhoudbare thema's voor de lange termijn.
Centrale punten
- Registratie via add_shortcode en clear callback functies
- Integratie in de editor, in widgets en in sjablonen met do_shortcode
- Beveiliging door escaping, attributen en validatie
- Onderhoud met kinderthema, documentatie en versiebeheer
- Prestaties met caching, zuinige query's en cache-invalidatie
Shortcodes in het thema: registratie en structuur
Ik plaats de registratie in het thema in de functies.php of in een kleine plugin die ik moet gebruiken als ik de functionaliteit wil scheiden van de lay-out. Elke callback functie retourneert een string en gebruikt geen echo, anders komt de output op onverwachte plaatsen terecht. Ik kies unieke voorvoegsels voor de naamgeving om conflicten met plugins te vermijden. Zo houd ik de code leesbaar en creëer ik een duidelijke volgorde, bijvoorbeeld /inc/shortcodes.php met een gerichte require_once in functions.php. Om te beginnen is een eenvoudige begroetings shortcode voldoende, die ik later stap voor stap uitbreid.
<?php
// /wp-content/themes/mijn-thema/functies.php
require_once get_template_directory() . '/inc/shortcodes.php';
<?php
// /wp-content/themes/mijn-thema/inc/shortcodes.php
functie mijn_groet_shortcode() {
return 'Hallo, welkom op mijn website!
}
add_shortcode('greeting', 'my_greeting_shortcode');
Gebruik shortcodes in de sjabloon: do_shortcode
Ik roep shortcodes in de sjabloon aan met do_kortcode wanneer ik inhoud in kop- en voetteksten of speciale sjablonen integreer. Dit houdt de editor overzichtelijk en ik houd terugkerende modules op een centrale plek. Ik documenteer sjabloonaanroepen in de code met een korte opmerking zodat anderen meteen weten welke shortcode hier wordt uitgevoerd. Voor dynamische parameters maak ik de shortcodestring aan in PHP en geef waarden veilig ontsnapt door. Deze methode werkt in elk sjabloonbestand zoals header.php, footer.php of paginasjablonen.
<?php
// In een sjabloonbestand
echo do_shortcode('[begroeting]');
echo do_shortcode('[colourbox color="green"]Leuke tekst[/colorbox]');
Attributen, inhoud en beveiliging
Ik stel attributen in met shortcode_atts en waarden beschermen met esc_html, esc_attr en esc_url. Dit voorkomt XSS en zorgt voor geldige HTML in alle uitvoer. Optioneel behandel ik inhoud ingesloten door een shortcode met wp_kses als ik alleen bepaalde tags wil toestaan. Voor kleuren accepteer ik alleen whitelist-waarden of controleer ik met regex zodat er geen scripts doorheen glippen. Met deze regels blijven shortcodes betrouwbaar en leveren ze voorspelbare output.
<?php
function my_colorbox_shortcode($atts, $content = null) {
$atts = shortcode_atts([
'color' => 'blauw',
], $atts, 'kleurvak');
$color = preg_match('/^#?[0-9a-fA-F]{3,6}$/', $atts['color']) ? $atts['kleur'] : 'blauw';
$inner = wp_kses($content, ['strong' => [], 'em' => [], 'a' => ['href' => []]);
$style = 'achtergrond:' . esc_attr($color) . ';padding:10px';
return '<div style="' . $style . '">' . $inner . '</div>';
}
add_shortcode('colourbox', 'my_colorbox_shortcode');
Praktijkvoorbeelden: huidig jaar, knop en data
Ik gebruik kleine helper shortcodes voor terugkerende inhoud, zoals de huidige Jaarknoppen of lijsten van aangepaste posttypes. Een jaar shortcode bespaart me onderhoudswerk in de footer of in tekstblokken. Ik voorzie knoppen van tekst, URL en kleur zodat redacteuren kunnen werken zonder de code te wijzigen. Voor gegevensuitvoer van CPT's beperk ik de query en cache ik de resultaten zodat de pagina snel blijft. Hier zijn drie korte fragmenten als basis.
<?php
// Jahr
add_shortcode('current-year', function() {
return date('Y');
});
// Button
add_shortcode('button', function($atts) {
$atts = shortcode_atts([
'text' => 'klik nu',
'url' => '#',
'kleur' => '#2d89ef',
], $atts, 'knop');
$text = esc_html($atts['tekst']);
$url = esc_url($atts['url']);
$color = esc_attr($atts['kleur']);
return '<a href="/nl/' . $url . '/" style="background:' . $color . ';padding:8px 18px;color:#fff;border-radius:4px;text-decoration:none">' . $text . '</a>';
});
Thema of plugin? Beslissingsondersteuning en migratie
Ik neem shortcodes op in de Thema als ze de lay-out beïnvloeden en in een plugin als ik ze onafhankelijk van het thema wil blijven gebruiken. Op deze manier verlies ik geen functies als ik van thema verander en houd ik de architectuur overzichtelijk. Voor de documentatie maak ik een overzicht van alle shortcodes met parameters, zodat redacteuren snel de juiste syntaxis kunnen vinden. Als ik later verhuis, exporteer ik de shortcodebestanden en vervang ik zorgvuldig de vereiste paden. De volgende tabel helpt bij de beslissing.
| Gebruik | Thema | Plugin |
|---|---|---|
| Binden aan lay-out | Hoog (bijv. Hero-Box) | Laag |
| Migratierisico | Hoger met themaverandering | Laagwordt behouden |
| Onderhoud/updates | Met thema-versie | Eigen release, flexibeler |
| Doelgroep | Kenmerken lay-out | Inhoud/functies |
Shortcodes gebruiken in de editor en in de blokeditor
Ik voeg shortcodes rechtstreeks toe in de klassieke editor als Tekst en gebruik het shortcodeblok in de blokeditor. Deze scheiding houdt de inhoud overzichtelijk en vermindert fouten bij het kopiëren. Voor editors documenteer ik voorbeelden direct in de backend, bijvoorbeeld als voorbeeldblok of opmerking in het sjabloon. Ik let op verschillen tussen editors in spatiëring en inline stijlen, omdat blokken soms extra wrappers toevoegen. Als u nadenkt over de keuze van een editor, vindt u in de vergelijking Blok editor vs klassiek handige tips.
Prestaties: Caching en schone query's
Ik houd shortcodes snel door rekenintensieve delen te cachen en de gegevenstoegang te beperken, waardoor de Laadtijd verlagingen. Voor terugkerende problemen gebruik ik Transients of WP Object Cache met een betekenisvolle sleutel. Ik beperk query's met posts_per_page, stel alleen verplichte velden in en vermijd dure COUNT-bewerkingen. Ik voeg breedte/hoogte en lui laden toe aan afbeeldingsuitvoer om de pagina sneller zichtbaar te maken. Voor dynamische onderdelen verwijder ik de cache zodra de inhoud verandert.
<?php
add_shortcode('latest-offers', function($atts) {
$key = 'sc_latest_offers_v1';
$html = wp_cache_get($key);
if ($html !== false) {
return $html;
}
$q = new WP_Query([
'post_type' => 'angebot',
'posts_per_page' => 5,
'no_found_rows' => true,
'fields' => 'all',
]);
ob_start();
if ($q->have_posts()) {
echo '<ul class="offers">';
while ($q->have_posts()) { $q->the_post();
echo '<li>' . esc_html(get_the_title()) . '</li>';
}
echo '</ul>';
wp_reset_postdata();
}
$html = ob_get_clean();
wp_cache_set($key, $html, '', 600);
return $html;
});
Foutbronnen snel opsporen en verhelpen
Ik activeer de Debug-modus en controleer of de shortcode correct is geregistreerd. Vaak geeft een wit scherm of ruwe tekst aan dat de functie niet laadt of echo gebruikt in plaats van return. Logboekvermeldingen onthullen onverwachte gegevenstypen, onjuiste attributen of ontbrekende escapes. In sjablonen test ik stap voor stap: eerst statische tekst, dan de shortcode, dan parameters. Als je systematisch te werk wilt gaan, gebruik dan de handleiding voor de WordPress Debug modus.
Werk update-veilig met kindthema
Ik maak mijn eigen shortcodes in de Kinderthema als ik niets kan of wil veranderen in het hoofdthema. Op deze manier blijven aanpassingen behouden tijdens thema-updates en heb ik controle over de laadvolgorde. Belangrijk: registreer het kinderthema correct, houd functions.php slank en neem alleen specifieke bestanden op. Voor gestructureerde projecten scheid ik shortcodes in /inc en documenteer ze met inline commentaar. Een compacte handleiding wordt geboden door de Child thema instructies.
Styling, semantiek en toegankelijkheid
Ik zorg voor schoon HTML en semantische tags zodat schermlezers de inhoud correct herkennen. Ik geef knoppen alleen weer als een tag met role="button" als het echt links zijn, anders kies ik voor echte knoppen. Ik houd kleurcontrasten hoog en stel focusstijlen in zodat toetsenbordgebruikers duidelijk kunnen zien waar ze zijn. Ik beperk inline stijlen en verplaats het ontwerp naar een CSS-bestand met duidelijke klassen. Dit houdt shortcodes flexibel en tegelijkertijd toegankelijk.
API-integratie en veilige integratie van externe gegevens
Ik haal externe gegevens op via wp_remote_get en cache ze zodat de pagina niet blijft hangen tijdens API time-outs. Ik controleer reacties op statuscodes, parseer JSON op een gecontroleerde manier en laat alleen de velden toe die echt nodig zijn. Bij mislukkingen geef ik een slanke fallback-uitvoer weer of verberg ik het blok. Voor gebruikerscontent verwijder ik gevaarlijke tags en valideer ik links grondig. Dit zorgt ervoor dat shortcodes stabiel blijven, zelfs als externe services fluctueren.
Activa alleen laden als ze in gebruik zijn
Ik laad CSS/JS voor shortcodes alleen als ze daadwerkelijk op de pagina verschijnen. Dit bespaart requests en houdt het kritieke pad CSS klein. Ik registreer stijlen en scripts centraal en enqueue ze in de callback of specifiek zodra ik de shortcode in de content herken. Belangrijk: schrijf nooit harde code in de header zonder na te denken, maar werk via de enqueue API's.
<?php // functions.php – Assets registreren add_action('wp_enqueue_scripts', function() { wp_register_style('my-shortcodes', get_template_directory_uri() . '/assets/shortcodes.css', [], '1.0');
wp_register_script('my-shortcodes', get_template_directory_uri() . '/assets/shortcodes.js', [], '1.0', true); });
// Alleen laden als aanwezig in de inhoud add_action('wp', function() { if (is_singular() && has_shortcode(get_post_field('post_content', get_queried_object_id()), 'button')) { wp_enqueue_style('my-shortcodes');
wp_enqueue_script('my-shortcodes'); } }); // Alternatief direct in de shortcode-callback oproepen: function my_assets_example_shortcode($atts, $content = null) { wp_enqueue_style('my-shortcodes'); return '<div class="my-box">' . wp_kses_post($content) . '</div>';
}
add_shortcode('my-box', 'my_assets_example_shortcode');
Shortcodes vs. directe functieaanroepen in de sjabloon
Ik maak een bewust onderscheid: voor vaste sjabloonmodules roep ik de functie liever rechtstreeks aan in plaats van een shortcode te parsen. Dit bespaart overhead, verhoogt de leesbaarheid en voorkomt verrassende filtereffecten. Shortcodes zijn bedoeld voor redactionele inhoud; sjablonen hebben baat bij duidelijke functieaanroepen met duidelijke parameters.
<?php
// In plaats van:
echo do_shortcode('[begroeting]');
// Beter in de sjabloon:
echo mijn_groet_shortcode();
Geneste shortcodes en opmaak
Ik houd rekening met geneste shortcodes en het automatisch invoegen van p- en br-tags. Als shortcodes andere inhoud omsluiten, blijf ik de inhoud binnen de shortcode renderen met do_shortcode, maar sta ik alleen toegestane tags toe. Ik verwijder lelijke p-tags rond shortcodes met shortcode_unautop als de markup anders zou worden verscheurd.
<?php function my_wrap_shortcode($atts, $content = null) { $inner = do_shortcode($content); // geneste shortcodes toestaan return '<div class="wrap">' . wp_kses_post($inner) . '</div>';
}
add_shortcode('wrap', 'my_wrap_shortcode');
// Optionele opmaakhulp
add_filter('the_content', 'shortcode_unautop');
Internationalisering en lokalisering
Ik houd shortcodes taalgeschikt: ik vertaal tekststrings met het thema tekstdomein en gebruik date_i18n voor datums. Op deze manier werken modules in meertalige omgevingen en blijven ze consistent als er van taal wordt gewisseld. Ik lokaliseer standaardteksten direct in de shortcode callbacks en ontsnap ze afhankelijk van de context.
<?php
// Thema voorbereid voor vertalingen
add_action('after_setup_theme', function() {
load_theme_textdomain('mijn-thema', get_template_directory() . '/languages');
});
// Gelokaliseerde begroeting
functie mijn_groet_shortcode() {
return esc_html__('Hallo, welkom op mijn website!', 'mijn-thema');
}
// Gelokaliseerd jaar
add_shortcode('huidig-jaar', function() {
return esc_html(date_i18n('Y'));
});
Cache ongeldig maken, varianten en sleutels
Ik plan caches op zo'n manier dat varianten netjes gescheiden zijn en inhoud snel verouderd raakt als er wijzigingen worden aangebracht. Attributen zoals limiet of taxonomie worden opgenomen in de sleutel. Als ik relevante posttypes opsla, verwijder ik specifiek de betreffende sleutels. In opstellingen met veel verkeer vertrouw ik op een persistent object cache backend en groepeer ik sleutels per kenmerk, zodat ik ze collectief kan legen.
<?php
add_shortcode('latest-offers', function($atts) {
$atts = shortcode_atts(['limit' => 5], $atts, 'latest-offers');
$limit = max(1, (int) $atts['limit']);
$key = 'sc_latest_offers_v1_' . $limit;
if (($html = wp_cache_get($key, 'mytheme')) !== false) {
return $html;
}
$q = new WP_Query([
'post_type' => 'angebot',
'posts_per_page' => $limit,
'no_found_rows' => true,
]);
ob_start();
if ($q->have_posts()) {
echo '<ul class="offers">';
while ($q->have_posts()) { $q->the_post();
echo '<li>' . esc_html(get_the_title()) . '</li>';
}
echo '</ul>';
wp_reset_postdata();
}
$html = ob_get_clean();
wp_cache_set($key, $html, 'mytheme', 600);
return $html;
});
// Cache invalidieren, wenn Angebote geändert werden
add_action('save_post_angebot', function() {
foreach ([1,5,10] as $limit) {
wp_cache_delete('sc_latest_offers_v1_' . $limit, 'mytheme');
}
});
Beveiliging verdiepen: sanitiser, toegestane attributen en rel/target
Ik breid shortcodes uit met verstandige maar veilige opties. Voor links beperk ik het doel tot _self/_blank en stel ik rel="noopener noreferrer" in voor nieuwe tabbladen. Ik controleer kleuren met sanitize_hex_color. Inhoud behandel ik contextgevoelig, voor ingesloten inhoud kies ik wp_kses_post of een restrictievere toestemmingslijst.
<?php
add_shortcode('button', function($atts, $content = null) {
$atts = shortcode_atts([
'text' => '',
'url' => '#',
kleur' => '#2d89ef',
'doel' => '_self',
], $atts, 'knop');
$text = $atts['text'] !== '' ? $atts['text'] : ($content ?: esc_html__('Klik nu', 'mijn-thema'));
$text = esc_html($text);
$url = esc_url($atts['url']);
$color = sanitise_hex_color($atts['color']) ?: '#2d89ef';
$target = in_array($atts['target'], ['_self','_blank'], true) ? $atts['target'] : '_self';
$rel = $target === '_blank' ? 'noopener noreferrer' : '';
$style = 'background:' . $colour . ';padding:8px 18px;color:#fff;border-radius:4px;text-decoration:none';
return '<a class="sc-button" href="/nl/' . $url . '/" style="' . esc_attr($style) . '" target="' . esc_attr($target) . '" rel="' . esc_attr($rel) . '">' . $text . '</a>';
});
Editor-, widget- en feed-contexten
Ik houd rekening met de context waarin de shortcode wordt uitgevoerd. Ik sta shortcodes expliciet toe in klassieke tekstwidgets en gebruik het shortcodeblok in de block widget editor. In feeds of in de zoekfunctie deactiveer ik bijzonder complexe shortcodes en geef ze leeg terug. Bovendien laad ik alleen activa op afzonderlijke pagina's als de shortcode in de inhoud voorkomt.
<?php
// Klassieke widgets: shortcodes activeren
add_filter('widget_text', 'do_shortcode');
// Vermijd dure uitvoer in feeds
add_shortcode('latest-offers-feed-safe', function($atts) {
if (is_feed()) {
return '';
}
// ... reguliere uitvoer
});
Afschaffing, migratie naar blokken en compatibiliteit
Ik plan de toekomst van mijn shortcodes: Als een tag wordt vervangen, stuur ik hem een tijdje door naar de nieuwe en kondig ik de verandering aan in de changelog. Als je vertrouwt op de blok-editor, kun je server-side blokken registreren met render_callback en intern dezelfde PHP-functie gebruiken als de shortcode. Op deze manier blijven beide paden netjes naast elkaar bestaan totdat de shortcode verloopt.
'', 'url' => '#'], $atts, 'old-button');
$text = $map['tekst'] ?: $content;
return do_shortcode("[button text="" . esc_attr($text) . '" url="" . esc_url($map['url']) . '"]');
});
// Later: volledig verwijderen
// remove_shortcode('old-button');
Tests en kwaliteitsborging
Ik valideer kritieke shortcodes met unit tests zodat refactorings geen verrassingen opleveren. In tests controleer ik of verplichte attributen zijn gevalideerd, of standaardwaarden zijn ingesteld en of de uitvoer correct is geëscaped. Voor HTML-uitvoer kies ik robuuste asserties (contains in plaats van exact match) zodat kleine opmaakwijzigingen niet alle tests breken. Ik test ook randgevallen zoals lege inhoud, ongeldige kleuren en zeer lange teksten.
assertStringContainsString('Hi', $out);
$this->assertStringContainsString('href="#"', $out);
}
publieke functie test_knop_geblokkeerd_ongeldige_kleur() {
$out = do_shortcode('[button color="javascript:alert(1)"]');
$this->assertStringNotContainsString('javascript:', $out);
}
}
Tot slot: Mijn compacte praktische overzicht
Ik registreer shortcodes duidelijk, lever ze veilig af en bewaar ze bij Caching snel. Voor redactioneel gebruik documenteer ik voorbeelden en zorg ik voor consistente parameters zodat iedereen ze met vertrouwen kan gebruiken. Layout-gerelateerde modules komen in het thema terecht, inhoud-gerelateerde functies in een plugin zodat de site op de lange termijn flexibel blijft. Met een child theme, debug logs en schone semantiek blijven ontwikkeling en onderhoud ontspannen. Het resultaat is een wordpress shortcode thema dat betrouwbaar rendert, eenvoudig te onderhouden is en content teams echte vrijheid geeft.


