@tomaszatoo/wp-shortcode
v0.0.2
Published
Angular library for rendering WordPress shortcodes as native Angular components
Maintainers
Readme
@tomaszatoo/wp-shortcode
Angular library for rendering WordPress shortcodes as native Angular components.
When you load WordPress post or page content via the REST API, shortcode tokens like [gallery id="123"] arrive as raw strings inside the HTML. This library parses those tokens and replaces them with real Angular components — with full change detection, dependency injection, and reactive @Input() binding.
For architecture details and workspace setup see the workspace README.
Installation
npm install @tomaszatoo/wp-shortcodeRequires Angular 17+.
Quick start
1. Create a shortcode component
Any standalone Angular component works. It receives the shortcode attributes and (for enclosing shortcodes) the inner HTML via standard @Input():
// gallery.component.ts
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-gallery',
template: `
<div class="gallery" [attr.data-id]="attrs['id']" [attr.data-size]="attrs['size']">
<!-- your gallery implementation -->
</div>
`,
})
export class GalleryComponent {
@Input() attrs: Record<string, string> = {}; // { id: '123', size: 'medium' }
@Input() inner = ''; // inner HTML (enclosing shortcodes only)
}2. Register shortcodes in your app config
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideShortcodes } from '@tomaszatoo/wp-shortcode';
import { GalleryComponent } from './gallery.component';
import { CaptionComponent } from './caption.component';
export const appConfig: ApplicationConfig = {
providers: [
provideShortcodes([
{ tag: 'gallery', component: GalleryComponent },
{ tag: 'caption', component: CaptionComponent },
]),
],
};3. Render shortcodes in a template
// post.component.ts
import { Component } from '@angular/core';
import { WpShortcodeComponent } from '@tomaszatoo/wp-shortcode';
@Component({
selector: 'app-post',
imports: [WpShortcodeComponent],
template: `<wp-shortcode [content]="post.content.rendered" />`,
})
export class PostComponent {
post = /* ... loaded from WP REST API */;
}That's it. Any [gallery], [caption], or other registered shortcode inside post.content.rendered is replaced by the corresponding Angular component.
Shortcode syntax support
| Format | Example | Supported |
|---|---|---|
| Self-closing | [gallery id="1" size="medium"] | ✅ |
| Self-closing with slash | [nextpage /] | ✅ |
| Enclosing | [caption align="left"]text[/caption] | ✅ |
| Nested (via inner recursion) | [outer][inner /][/outer] | ✅ |
| Escaped | [[gallery]] | ✅ (left as-is) |
Attribute formats
[tag name="double quoted"]
[tag name='single quoted']
[tag name=unquoted]
[tag "positional"] ← stored as key "0", "1", …Enclosing shortcodes
When WordPress uses [tag]content[/tag] syntax, the inner HTML is passed to your component's inner input as a raw string. Your component decides what to do with it:
// caption.component.ts
import { Component, Input } from '@angular/core';
import { WpShortcodeComponent } from '@tomaszatoo/wp-shortcode';
@Component({
selector: 'app-caption',
imports: [WpShortcodeComponent],
template: `
<figure [class]="attrs['align']">
<!-- recursively process shortcodes inside the caption -->
<wp-shortcode [content]="inner" />
<figcaption>{{ attrs['caption'] }}</figcaption>
</figure>
`,
})
export class CaptionComponent {
@Input() attrs: Record<string, string> = {};
@Input() inner = '';
}Using <wp-shortcode [content]="inner"> inside your component enables recursive/nested shortcode processing.
Lazy-loaded feature modules
provideShortcodes() uses Angular's multi provider — you can call it as many times as needed, including inside lazy routes:
// feature.routes.ts
export const routes: Routes = [
{
path: 'shop',
providers: [
provideShortcodes([
{ tag: 'product', component: ProductComponent },
{ tag: 'add-to-cart', component: AddToCartComponent },
]),
],
loadComponent: () => import('./shop/shop.component'),
},
];Programmatic registration
Use ShortcodeRegistry directly when you need to register components at runtime (e.g. after a dynamic import):
import { inject } from '@angular/core';
import { ShortcodeRegistry } from '@tomaszatoo/wp-shortcode';
const registry = inject(ShortcodeRegistry);
registry.register('my-tag', MyComponent);Parser utilities
The parser is exported as a standalone pure function — useful for pre-processing, server-side rendering, or building your own renderer:
import { parseShortcodes, parseAttributes } from '@tomaszatoo/wp-shortcode';
const segments = parseShortcodes('<p>Hello</p>[gallery id="1"]<p>World</p>');
// [
// { type: 'html', html: '<p>Hello</p>' },
// { type: 'shortcode', tag: 'gallery', attrs: { id: '1' }, inner: '' },
// { type: 'html', html: '<p>World</p>' },
// ]
const attrs = parseAttributes('id="1" size="medium"');
// { id: '1', size: 'medium' }Security note
HTML segments between shortcodes are rendered via Angular's DomSanitizer.bypassSecurityTrustHtml(). This is intentional — WordPress content regularly contains iframes (YouTube, Vimeo), plugin-injected scripts, and custom attributes that Angular's default sanitizer would strip.
Only pass content from sources you control (your own WordPress instance). Never pass user-submitted or third-party HTML directly.
Layout note
Each HTML segment is wrapped in a <div class="wp-html-segment">. If this interferes with your layout (e.g. inside a flex or grid container), you can make the wrapper transparent:
wp-shortcode .wp-html-segment {
display: contents;
}API reference
provideShortcodes(definitions)
provideShortcodes(definitions: ShortcodeDefinition[]): ProviderRegisters shortcode → component mappings. Safe to call multiple times.
interface ShortcodeDefinition {
tag: string; // e.g. 'gallery'
component: Type<unknown>;
}<wp-shortcode>
@Component({ selector: 'wp-shortcode', ... })
export class WpShortcodeComponent {
content = input<string>(''); // HTML string containing WP shortcodes
}ShortcodeRegistry
@Injectable({ providedIn: 'root' })
export class ShortcodeRegistry {
resolve(tag: string): Type<unknown> | null;
register(tag: string, component: Type<unknown>): void;
}parseShortcodes(content)
parseShortcodes(content: string): Segment[]
type Segment = HtmlSegment | ShortcodeToken;
interface HtmlSegment { type: 'html'; html: string; }
interface ShortcodeToken { type: 'shortcode'; tag: string; attrs: Record<string, string>; inner: string; }parseAttributes(str)
parseAttributes(str: string): Record<string, string>Building & publishing
# build the library
ng build
# publish from the dist folder
cd ../../dist/tomaszatoo/wp-shortcode
npm publishLicense
MIT
