Convert Markdown to HTML in your WordPress Projects

Learn how to use markdown in your WordPress projects. Attach markdown content to posts as a custom field, and convert it to HTML in the frontend. We’ll use the open source “Parsedown” PHP library to do the hard work of converting markdown to HTML. So… all we need to do is join-the-dots using Advanced Custom Fields and a bit of PHP in our custom child theme.

Markdown Source

## Markdown Content

Markdown is a lightweight markup language for creating formatted text using a plain-text editor. [Wikipedia](https://en.wikipedia.org/wiki/Markdown)

### Fairytale Ipsum

Once upon a time... The clouds were lifting and the girl was **ready for anything**. On a bright and sunny dawn a child took a walk into the wood. The girl was taking a pie to their grandma.

### More Gobbledygook Ipsum Generators...

* [Corporate Ipsum Generator](https://flipsum-ipsum.net/ipsum/corporate/)
* [Gobbledygook Poetry](https://flipsum-ipsum.net/ipsum/jabberwocky/)
* [Movie Quotes Ipsim](https://flipsum-ipsum.net/ipsum/hollywood/)

Converted to HTML

Markdown Content

Markdown is a lightweight markup language for creating formatted text using a plain-text editor. Wikipedia

Fairytale Ipsum

Once upon a time... The clouds were lifting and the girl was ready for anything. On a bright and sunny dawn a child took a walk into the wood. The girl was taking a pie to their grandma.

More Gobbledygook Ipsum Generators...

How It’ll Work

We’re going to use Advanced Custom Fields (ACF) to create a new (post meta) field attached to “Post” objects. This ACF Field will be a “text area” type, so it can hold multiple lines of markdown. Then we’ll create a shortcode so we can pick up a post’s markdown field and either render its source, or convert it to HTML.

Getting Started

Before you start, make sure you’re using a custom child theme on your WordPress site, so you can edit functions.php.

In your custom child theme, create a new file called wpt-markdown.php and create a folder called wpt-markdown. Go into the wpt-markdown folder and create an empty file called wpt-markdown.css.

Copy and paste the following into wpt-markdown.php, acting as a placeholder.

<?php

/**
 * WP Tutorials Markdown (wptmd)
 *
 * https://wp-tutorials.tech/add-functionality/convert-markdown-to-html-in-your-wordpress-projects/
 *
 */

if (!defined('WPINC')) {
	exit('Do NOT access this file directly.');
}

// Our code will go in here...
// ...
// ...

Next, open your child theme’s functions.php and add the following couple of lines to it.

/**
 * WP Tutorials Markdown.
 */
require_once dirname(__FILE__) . '/wpt-markdown.php';

That’s our basic scaffold code sorted out. Now download the latest version of Parsedown.

  • Go to Parsedown on GitHub.
  • Go to the Releases.
  • Download the current release zip file.
  • Extract the zip file.
  • Copy Parsedown.php into the wpt-markdown folder.
Parsedown Releases

Create the ACF Field

Make sure you’ve got the “Advanced Custom Fields” plugin installed. In the WP Admin area, go to Custom fields. If you don’t already have a field group for your post meta, create one now. Here, we’re using a field group called Extra Post Fields – you can call your field group whatever you like.

Add a new field to your field group…

  • Label: Whatever you want, e.g. “Markdown Content”
  • Name: markdown_content – we’ll refer to this from our PHP code
  • Field Type: “Text Area”
  • Instructions: Optional help-text for content creators

Save your field group, edit a Post on your site and you should see there’s a new meta box where you can enter markdown content. It’ll be at the bottom of the page when you’re editing your post.

ACF Custom Field for Markdown
Markdown Custom Field
Extra Post Fields - Markdown Content Field
Custom ACF Field for markdown content

The Main PHP Bit

Copy and paste the following into the wpt-markdown.php file you created earlier – completely replacing the scaffold code that was already in there.

<?php

/**
 * WP Tutorials Markdown (wptmd)
 *
 * https://wp-tutorials.tech/add-functionality/convert-markdown-to-html-in-your-wordpress-projects/
 *
 */

if (!defined('WPINC')) {
	exit('Do NOT access this file directly.');
}

// This is the name of the custom post meta field, usually defined in ACF.
// If you giv eyour custom post meta field a different name, you'll 'need to
// change it here, too.
const WPTMD_DEFAULT_POST_META_KEY = 'markdown_content';

function wptmd_include_dependencies() {
	global $wptmd_have_deps_been_loaded;

	if (is_null($wptmd_have_deps_been_loaded)) {
		$base_dir = trailingslashit(dirname(__FILE__) . '/wpt-markdown');

		// Parsedown: https://github.com/erusev/parsedown
		// Only load the Parsedown.php file if there isn't already a class
		// called "Parsedown". The class might have been already provided by
		// another plugin.
		if (!class_exists('Parsedown')) {
			require_once $base_dir . 'Parsedown.php';
		}

		$base_url = get_stylesheet_directory_uri() . '/' . pathinfo(__FILE__, PATHINFO_FILENAME) . '/';
		$version = wp_get_theme()->get('Version');

		wp_enqueue_style('wptmd', $base_url . 'wpt-markdown.css', '', $version);

		$wptmd_have_deps_been_loaded = true;
	}
}

function wptmd_get_parser() {
	global $wptmd_parsedown;

	if (is_null($wptmd_parsedown)) {
		$wptmd_parsedown = new Parsedown();

		// You can make the parser more strict, if you're rendering untrusted
		// user input. Just uncomment this...
		// $wptmd_parsedown->setSafeMode(true);
	}

	return $wptmd_parsedown;
}

/**
 * Render the markdown as an HTML string.
 */
function wptmd_do_shortcode($atts) {
	$html = '';

	if (is_admin()) {
		// Don't do anything.
	} elseif (wp_doing_ajax()) {
		// Don't do anything.
	} else {
		// Enqueue our frontend assets.
		wptmd_include_dependencies();

		$args = shortcode_atts(
			array(
				'field_name' => WPTMD_DEFAULT_POST_META_KEY,
				'class' => '',
				'source' => false,
			),
			$atts
		);

		$post_id = get_the_id();
		$show_source = filter_var($args['source'], FILTER_VALIDATE_BOOLEAN);
		$source = get_post_meta($post_id, $args['field_name'], true);

		$field_name = $args['field_name'];
		if (empty($field_name)) {
			$source = 'No custom field specified.';
		} elseif (empty($source)) {
			$source = sprintf('No markdown found in the %s custom field.', $field_name);
		} else {
			// OK.
		}

		$classes = array('wptmd');
		if (!empty($args['class'])) {
			$classes[] = $args['class'];
		}

		if ($show_source) {
			$classes[] = 'source';

			// Render the markdown source in a <pre> element.
			$html .= sprintf(
				'<pre class="%s">%s</pre>',
				esc_attr(implode(' ', $classes)),
				$source
			);
		} else {
			$classes[] = 'html';

			$parser = wptmd_get_parser();

			// Render the markdown source in a <div> element.
			$html .= sprintf(
				'<div class="%s">%s</div>',
				esc_attr(implode(' ', $classes)),
				$parser->text($source)
			);
		}
	}

	return $html;
}
add_shortcode('wpt_markdown', 'wptmd_do_shortcode');

The code breaks down quite cleanly.

We’ve got two support functions…

  • wptmd_include_dependencies()
  • wptmd_get_parser()

…and our main function, which renders our shortcode:

  • wptmd_do_shortcode()

The first time wptmd_include_dependencies() is called, it enqueues our frontend CSS file, and it requires (includes) the Parsedown.php file.

Main Logic Breakdown

The wptmd_do_shortcode() function is our main code block, and it should be really easy to follow:

  • Check we’re OK to run (i.e. we’re not in the admin part of WordPress)
  • Include/enqueue dependencies
  • Parse the shortcode’s input parameters
  • Get the current post id
  • Get the content of our post meta field (markdown_content)
  • If it’s empty…
    • Set the HTML output to be a useful diagnostic message
  • Build a small array of CSS classes to add to the container element we’re about to render
  • If we’re rendering the Markdown source…
    • Output the source wrapped in a <pre> element, with our classes
  • else…
    • Get/Create an instance of the Parsedown parser
    • Call Parsedown->text( $source )
    • Wrap the output in a <div> element, with our classes
  • Return the HTML back to WordPress

Basic CSS Styles

We only need some simple CSS here, to keep things tidy on the frontend. Paste the following into wpt-markdown/wpt-markdown.css to get started.

/**
 * WP Tutorials Markdown frontend.
 */

.wptmd {
	background-color:  white;
	padding:  1rem;
	border-radius: 0.5rem;
	box-shadow: 0 0 1em #44444422;
}

.wptmd.source {
	font-family: "Lucida Console", Courier, monospace;
	white-space: pre-wrap;
}

Finish & Deploy

That’s about it. Edit a post, add some markdown into the markdown_content field, and add a shortcode to your content. Easy 😎👍

Extending the Code

  • Instead of using shortcodes, you could modify the PHP code to hook the WordPress the_content filter and automatically render markdown content before (or after) all your posts.
  • Use the object cache to store rendered HTML, so you don’t have to call Parsedown->text() on every page load. You would need to find an elegant way of generating object cache keys though.
Examples of WordPress Markdown shortocdes
Example Markdown shortcodes

Leave a Comment

Your email address will not be published. Required fields are marked *