Creating a Kanban board with WordPress

You know me, I love to use WordPress not only as a blog but as a full fledged CMS and lately as an app backend using the WordPress REST API. So today I want to share a proof of concept creating a Vue.js app to create a Kanban board.


I want to keep track of my weekly tasks at work and for personal use. At the end of the week I usually have to report highlights of the past days, this is a great tool to do that.

I could've used Trello, or similar, but what's the fun in that? 😁 Plus, owning your data is nice for privacy 🕵️‍♂️

What's the plan?

The plan today is to create a WordPress Theme that has a single page Vue.js app which talks to the WordPress REST API to pull and create data. WordPress already has out of the box authentication (/wp-admin), so as long as you're logged in you're good go. You wouldn't know you're inside a WordPress instance.

👷‍♂️ How will I structure the data?

Easy. I'll use a Custom Post Type and a Custom Taxonomy to create my data structure.

  • Post type: item (title, content and custom meta data)
  • Taxonomy: board

I'll have N items assigned to 1 or more boards. Each item will have a custom metadata field called status to know in what column (to do, in progress or done) the item is in.

Here's the quick code to create both post type and taxonomy:


function custom_post_type() {

	$labels = array(
	$args = array(
		'label'                 => __( 'Item', 'text_domain' ),
		'description'           => __( 'Item Description', 'text_domain' ),
		'labels'                => $labels,
		'supports'              => array( 'title', 'editor', 'custom-fields' ),
		'taxonomies'            => array(),
		'hierarchical'          => false,
		'public'                => true,
		'show_ui'               => true,
		'show_in_menu'          => true,
		'menu_position'         => 5,
		'menu_icon'             => 'dashicons-clipboard',
		'show_in_admin_bar'     => true,
		'show_in_nav_menus'     => true,
		'show_in_rest'          => true,
		'can_export'            => true,
		'has_archive'           => true,
		'exclude_from_search'   => false,
		'publicly_queryable'    => true,
		'capability_type'       => 'post',
	register_post_type( 'item', $args );

add_action( 'init', 'custom_post_type', 0 );

function custom_taxonomy() {

	$labels = array(
	$args = array(
		'labels'                     => $labels,
		'hierarchical'               => false,
		'public'                     => true,
		'show_ui'                    => true,
		'show_admin_column'          => true,
		'show_in_nav_menus'          => true,
		'show_in_rest'               => true,
		'show_tagcloud'              => true,
	register_taxonomy( 'board', array( 'item' ), $args );

add_action( 'init', 'custom_taxonomy', 0 );
Code language: HTML, XML (xml)

🏗 Create the Vue app

One of the fastest ways to get you up and running without worrying about the build, setup, local server, production bundles... is Vue CLI. Done ✅ If you haven't checked it out yet, do it. It's so easy to use and powerful, yet it allows for customization.

🤔 How to let the app know the REST API url?

WordPress provides rest_url() which retrieves the URL to a REST endpoint.

I've searched for a ways to set a data attribute the app div but no luck (disclaimer, I'm no Vue.js expert. Could it be done?)

Well, this works so I'll go with it. Just adding a global variable with my rest URL and accessing it within the Vue app later:

  window.kanban_ = { restUrl: '<?=rest_url()?>' }
<main id="app"></main>Code language: HTML, XML (xml)

😍 Create custom API endpoints

Here's the fun part. You can create any endpoint you want. Here's what I created, get all the boards with items, create an item, update an item... Everything I need to fire POST/GET requests from the FE to the WordPress backend. Here's a snippet of the full code:

add_action( 'rest_api_init', function () {
  register_rest_route( 'kanban/v1', '/all-boards', array(
    'methods' => 'GET',
    'callback' => 'get_all_boards',
  ) );

  register_rest_route( 'kanban/v1', '/board/(?P<id>\d+)', array(
    'methods' => 'GET',
    'callback' => 'get_board'
  ) );

  register_rest_route( 'kanban/v1', '/create-item', array(
    'methods' => 'POST',
    'callback' => 'create_item',
  ) );

  register_rest_route( 'kanban/v1', '/update-item-status', array(
    'methods' => 'POST',
    'callback' => 'update_item_status',
  ) );

  register_rest_route( 'kanban/v1', '/update-item', array(
    'methods' => 'POST',
    'callback' => 'update_item',
  ) );
} );Code language: PHP (php)

📖 Creating, querying, updating...

Nothing new really, just regular WordPress functions in the callbacks defined above. Such as using wp_insert_post function and using the POST data sent from the Vue app:

function create_item ($data) {
  $post = array(
    'post_title'    => wp_strip_all_tags( $data['post_title'] ),
    'post_content'  => $data['post_content'],
    'post_status'   => 'publish',
    'post_type' => 'item'

  $post_id = wp_insert_post( $post );
  add_post_meta($post_id, 'status', $data['status'], true );
}Code language: PHP (php)

💡How to add custom meta data to WordPress REST API

🔥 Fire fetch requests from Vue

Super easy and straight forward to do this, no much Vue involved but vanilla JS.

Here's the GET request to Fetch API all (or a specific) Kanban board:

fetchResources: function() {
  let endpoint = window.kanban_.restUrl

  if ( this.$route.params.board_id ) {
    endpoint += `${endpoints.board}/${this.$route.params.board_id}`
  } else {
    endpoint += endpoints.allBoards

  this.showLoading = true;

    .then(response => response.json())
    .then(boards => {
      this.boards = boards;
      this.showLoading = false;

      if ( this.$route.params.board_id ) {
        const metaTitle = this.boards[0].name
        document.title = document.title.replace('%board%', metaTitle);
}Code language: JavaScript (javascript)

Doing a POST request is as trivial as:

createItem: function() {  
  const data = {
    board: this.newItem.board,
    post_title: this.newItem.title,
    post_content: this.newItem.content,
    status: 1

  const options = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    body: JSON.stringify(data),

  fetch(`${window.kanban_.restUrl}${endpoints.createItem}`, options)
    .then(() => {
      // Refresh everything
}Code language: JavaScript (javascript)

🛀 What's left?

Just the UI for the app. Icons, components, just plain layout prototyping. The most complicated part creating the API and doing HTTP requests is done.

I usually rely on BootstrapVue when I don't need anything custom, it provides a lot out of the box for these type of projects.

👇 Grab the code

Head over to Github and grab the theme and enjoy!

Leave a Reply

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