Add Social Share Buttons in WordPress With a Shortcode

Share buttons for Facebook, X and LinkedIn usually come with a plugin that loads its own scripts and tracking on every page. When all you need are three links under each post, or on every card in a post grid, a WordPress shortcode is enough.

This post started as a shortcode for social share buttons in an Oxygen repeater. The rewritten [social_media] shortcode still works there, and also in post content, a Shortcode block in a block theme template, a Query Loop and other page builder repeaters. In a loop, each card shares its own post.

The snippet

<?php
/**
 * [social_media] share buttons (Facebook, X, LinkedIn) for the current post.
 * Usage: [social_media], [social_media networks="x,linkedin"], [social_media id="42"]
 * Works in post content, a Shortcode block, Query Loops and page builder repeaters.
 */

function dpc_social_share_networks(): array {
	return array(
		'facebook' => array(
			'label'   => 'Facebook',
			'url'     => 'https://www.facebook.com/sharer/sharer.php?u={url}',
			'viewbox' => '0 0 24 28',
			'path'    => 'M19.5 2c2.484 0 4.5 2.016 4.5 4.5v15c0 2.484-2.016 4.5-4.5 4.5h-2.938v-9.297h3.109l0.469-3.625h-3.578v-2.312c0-1.047 0.281-1.75 1.797-1.75l1.906-0.016v-3.234c-0.328-0.047-1.469-0.141-2.781-0.141-2.766 0-4.672 1.687-4.672 4.781v2.672h-3.125v3.625h3.125v9.297h-8.313c-2.484 0-4.5-2.016-4.5-4.5v-15c0-2.484 2.016-4.5 4.5-4.5h15z',
		),
		'x'        => array(
			'label'   => 'X',
			'url'     => 'https://x.com/intent/tweet?url={url}&text={title}',
			'viewbox' => '0 0 24 24',
			'path'    => 'M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z',
		),
		'linkedin' => array(
			'label'   => 'LinkedIn',
			'url'     => 'https://www.linkedin.com/sharing/share-offsite/?url={url}',
			'viewbox' => '0 0 24 28',
			'path'    => 'M3.703 22.094h3.609v-10.844h-3.609v10.844zM7.547 7.906c-0.016-1.062-0.781-1.875-2.016-1.875s-2.047 0.812-2.047 1.875c0 1.031 0.781 1.875 2 1.875h0.016c1.266 0 2.047-0.844 2.047-1.875zM16.688 22.094h3.609v-6.219c0-3.328-1.781-4.875-4.156-4.875-1.937 0-2.797 1.078-3.266 1.828h0.031v-1.578h-3.609s0.047 1.016 0 10.844v0h3.609v-6.062c0-0.313 0.016-0.641 0.109-0.875 0.266-0.641 0.859-1.313 1.859-1.313 1.297 0 1.813 0.984 1.813 2.453v5.797zM24 6.5v15c0 2.484-2.016 4.5-4.5 4.5h-15c-2.484 0-4.5-2.016-4.5-4.5v-15c0-2.484 2.016-4.5 4.5-4.5h15c2.484 0 4.5 2.016 4.5 4.5z',
		),
	);
}

add_shortcode( 'social_media', 'dpc_social_share_shortcode' );

function dpc_social_share_shortcode( $atts ): string {
	$atts = shortcode_atts(
		array(
			'networks' => 'facebook,x,linkedin',
			'id'       => 0,
		),
		$atts,
		'social_media'
	);

	$post = absint( $atts['id'] ) ? get_post( absint( $atts['id'] ) ) : get_post();
	if ( ! $post || ! is_post_publicly_viewable( $post ) ) {
		return '';
	}

	$url   = rawurlencode( get_permalink( $post ) );
	$title = rawurlencode( html_entity_decode( wp_strip_all_tags( get_the_title( $post ) ), ENT_QUOTES, get_bloginfo( 'charset' ) ) );
	$all   = dpc_social_share_networks();
	$links = '';

	foreach ( array_map( 'trim', explode( ',', strtolower( (string) $atts['networks'] ) ) ) as $key ) {
		if ( ! isset( $all[ $key ] ) ) {
			continue;
		}
		$network = $all[ $key ];
		$links  .= sprintf(
			'<a class="dpc-share__link dpc-share__link--%1$s" href="%2$s" target="_blank" rel="noopener noreferrer" aria-label="%3$s"><svg viewBox="%4$s" width="32" height="32" aria-hidden="true" focusable="false"><path d="%5$s"/></svg></a>',
			esc_attr( $key ),
			esc_url( str_replace( array( '{url}', '{title}' ), array( $url, $title ), $network['url'] ) ),
			/* translators: %s: network name, e.g. Facebook. */
			esc_attr( sprintf( __( 'Share on %s', 'dpc' ), $network['label'] ) ),
			esc_attr( $network['viewbox'] ),
			esc_attr( $network['path'] )
		);
	}

	if ( '' === $links ) {
		return '';
	}

	dpc_social_share_style();

	return '<div class="dpc-share">' . $links . '</div>';
}

/** The small stylesheet, once per page and only on pages that show the buttons. */
function dpc_social_share_style(): void {
	if ( wp_style_is( 'dpc-share', 'enqueued' ) ) {
		return;
	}

	wp_register_style( 'dpc-share', false, array(), '1.0' );
	wp_add_inline_style(
		'dpc-share',
		'.dpc-share{display:flex;flex-direction:row;flex-wrap:wrap;gap:8px}'
		. '.dpc-share__link{display:inline-flex;color:inherit;line-height:0}'
		. '.dpc-share__link svg{width:32px;height:32px;fill:currentColor}'
		. '.dpc-share__link:hover,.dpc-share__link:focus-visible{opacity:.75}'
	);
	wp_enqueue_style( 'dpc-share' );
}

Use it

  • [social_media] shows all three buttons for the current post.
  • [social_media networks="x,linkedin"] picks networks and their order. Unknown names are ignored.
  • [social_media id="42"] shares another post, for example on a landing page.
  • In Oxygen, add a Shortcode element inside the Repeater and enter [social_media]. In a block theme, add a Shortcode block inside the Query Loop’s Post Template.

How it works

  • The permalink and title are encoded with rawurlencode() and every attribute is escaped, so titles with & or quotes cannot break the links.
  • X uses x.com/intent/tweet and LinkedIn the current share-offsite URL. The old twitter.com/home?status= link from the first version no longer opens a share dialog.
  • Links open in a new tab with rel="noopener noreferrer", have an accessible name such as “Share on Facebook”, and the icons are hidden from screen readers.
  • The first version gave every icon the same id, so a repeater with ten posts had thirty duplicate IDs, and printed a <style> tag for every item. Now there are only classes, and a small stylesheet is added once per page, only on pages that show buttons.
  • Drafts and private posts print nothing, so you never share a link that visitors cannot open.

Add it with Scripts Organizer

In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the code into the PHP editor and publish. Leave Action/Hook empty.

Everywhere registers the shortcode on every request, including the builder and block editor previews. The CSS lives inside the PHP on purpose: Scripts Organizer prints Header and Footer code only with the Conditions trigger, and the shortcode adds its styles itself where it is used.

The same code also works unchanged in a small plugin or an mu-plugin.

Customise it

  • Another network: add an entry to dpc_social_share_networks() with a share URL using {url} and {title}, and an SVG path. For example WhatsApp with https://wa.me/?text={title}%20{url}.
  • Brand colours: add rules such as .dpc-share__link--facebook{color:#1877f2} to the inline CSS.
  • Size: change 32px in the CSS.

Gotchas

  • Networks read the page’s Open Graph tags for the preview image and text. Without an SEO plugin that prints them, the preview may be plain.
  • If another plugin also registers [social_media], the one loaded last wins. Rename the tag in add_shortcode() and shortcode_atts().

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.