Posted on Leave a comment

Using a Highlight.js with GatsbyJS & WordPress

GatsbyJS is awesome because it allows developers to quickly design, develop and deploy projects. Gatsby sites are automatically optimized for speed and allows us to focus on building something great.

Why use a Code Syntax Highlighter?

If your personal website is littered with pre and code tags – you might want to consider using a code syntax highlighter.

Syntax highlighters are abundant, easy to use, improve readability and really up your dev game.

With syntax highlighting

Without syntax highlighting

I think the images speak for themselves.

Highlight.js

Highlight.js homepage has a interactive demo for syntax highlighting

As mentioned above – there are plenty of options out there when it comes to code syntax highlighters.

We are going to choose highlight.js because it is well-known, easy to use and comes with lots of theme styles.

Setup

First, download highlight,js from npm.

$ npm i highlight.js

Next create a helper file at /src/utils/helper.js.

$ touch src/utils/helper.js

Import the library

// /src/utils/helper.js

import hljs from "highlight.js"

Pick a code theme

Highlight.js has lots of themes available – you can find them all by visiting the official repository, the homepage or running ls in the module’s styles folder.

I chose ‘atom-one-dark’.

// /src/utils/helper.js

import hljs from "highlight.js"
import "highlight.js/styles/atom-one-dark.css"

Configuration

Unless you have some built in meta data or class names which signify the language in your code blocks – you will probably want to use the auto-detect feature.

Trouble is, highlight.js has over 180 languages and detection can be tricky.

I overcame this by narrowing the search to just the languages I know I use in these blocks

// /src/utils/helper.js

...

hljs.configure({
  languages: ["php", "javascript", "json", "bash", "scss"],
})

You can see all the possible options by running hljs.listLanguages() in the console.

Building our code highlighter

Finally we are going to create our highlightCode() function which simple finds all code tags nested within a pre tag and “highlight it”.

// /src/utils/helper.js

...

const highlightCode = () => {
  const codes = document.querySelectorAll("pre > code")
  for (let key in codes) {
    if (typeof codes[key] === "object") {
      hljs.highlightBlock(codes[key])
    }
  }
}

Essentially we are looping through the elements, double checking it is an object then replacing the node with a new element created by the highlighter.

Wrapping up

Finally export the helper – the final result should be:

// /src/utils/helper.js

import hljs from "highlight.js"
import "highlight.js/styles/atom-one-dark.css"

hljs.configure({
  languages: ["php", "javascript", "json", "bash", "scss"],
})

const highlightCode = () => {
  const codes = document.querySelectorAll("pre > code")
  for (let key in codes) {
    if (typeof codes[key] === "object") {
      hljs.highlightBlock(codes[key])
    }
  }
}

export default { highlightCode }

Using our helper function

The new helper we wrote is great but how do we use it? See this was the tricky bit for me.

GatsbyJS + WordPress work together by querying your WordPress database for the appropriate data.

A post’s content is passed as a large string of HTML and the only way to use that HTML in react is to use dangerouslySetInnerHTML.

dangerouslySetInnerHTML

dangerouslySetInnerHTML is React’s replacement for using innerHTML in the browser DOM. In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack.

https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml

As per the offical react documentation this property name is purposefully scary to remind you its dangerous to set the innerHTML unless your careful.

Luckily our content was written in the WordPress CMS and has already been sanitized for our viewing pleasure.

Highlighting the code blocks

Finally we are going to use the helper we created.

I’m going to assume you have a running Gatsby site with a template setup to display your single posts. If not – the GatsbyJS + WordPress documentation is wonderful and got me started quickly.

Your post will look something like this – where we are creating a content div and passing our posts content via the dangerouslySetInnerHTML property.

// /src/templates/blog-post.js

Import React, { useEffect } from 'react'

export default function BlogPost({ data }) {
  const post = data.allWpPost.nodes[0]

return (
    <Layout>
      <div id="single">
        <h1 className="title section-header">{post.title}</h1>
        <div
          id="content"
          dangerouslySetInnerHTML={{
            __html: post.content,
          }}
        />
      </div>
    </Layout>
  )
}

Most important to understand is we are passing the HTML of our post directly to div#content.

UseEffect

UseEffect is a react lifecycle method – essentially a spot/time in the code where we can run our own functions. Because we are forced to use dangerouslySetInnerHTML – we cannot access or modify the post’s content before hand.

We can however use UseEffect to run our code highlighter after the component is loaded into the DOM and the content created.

Add import your helper and run the highlightCode function in the useEffect block.

// /src/templates/blog-post.js

Import React, { useEffect } from 'react'

...

 useEffect(() => {
    helper.highlightCode()
  })

  return (
...

Your code blocks should now look like a text editor – improving the readability and aesthetic of your blog posts.

Posted on Leave a comment

Gatsby + WordPress Menu API

GatsbyJS and WordPress work beautifully together – you get to keep using the familiar WordPress backend while running react in the front.

Getting Gatsby to work with the WordPress menu API was fun so I thought I’d share how I did it.

Prerequisites

This post is geared toward developers with intermediate React and WordPress experience.

Follow this Gatsby + WordPress tutorial to quickly get started if you don’t already have a Gatsby application running.

Overview

Our objective is to get our Gatsby site to play nice with the WordPress menu API.

To achieve this, we must:

  • Build our query in the graphQL IDE
  • Build our Menu react component
  • Implement logic for dealing with nested navigation items

At the end of this – we will have a navigation menu that works seamlessly with the WordPress backend.

Setup

Gatsby uses graphQL for querying data from an API, so download and install the WPGraphQL & WPGatsby plugins on your existing wordpress site.

Next add the following to your gatsby-config.js file located at the root of your gatsby site.

/gatsby-config.js


{
  plugins: [
    {
      resolve: `gatsby-source-wordpress-experimental`,
      options: {
        url: `http://gerrg.com/graphql`,
      },
    },
  ],
}

Finally run gatsby develop and “experience the magic”.

Any issues – checkout the quick start instructions here.

gatsby develop

After running gatsby develop we can visit our site at http://localhost:8000 and visit our graphQL IDE at http://localhost:8000/___graphql.

The graphQL IDE

My favorite thing about graphQL (besides the simple syntax) is the IDE – this allows your to quickly build queries, explore the schema and experiment.

Using the graphQL IDE to quickly form a query for all posts from my wordpress site.

Getting WPMenu Items

The WPGraphql plugin we installed on our existing wordpress site allows our to query the WP_Menu API – this is what allows our gatsby site to work with Menus created in the WordPress admin section at: Appearance > Menus.

The WordPress admin section for building menus.

Copy the following query into your IDE to ensure you can get your menu items properly:

query MyQuery {
  wpMenu {
    id
    menuItems {
      nodes {
        parentId
        label
        path
        childItems {
          nodes {
            path
            label
          }
        }
      }
    }
  }
}

Running the query should return an object with a list of menu items – including parentID, path, label and children (if any).

The results of your menu item query should look similar to this image

Assuming you’ve got the proper data returned, now we are set to build a Menu component and feed it our queried data.

Creating our Menu in React

Now that we have a query, we are going to want to bring that data into our Gatsby application.

First, create a basic Header component at src/components/header.js.

// src/components/header.js

import React from "react"

const Header = () => {
  // query data here

  return (
    <header className="header">
        <h1>Gatsby + WordPress Menu's<h1>
    </header>
  )
}

export default Header

Since header is not a “template” we must use useStaticQuery to contact the WordPress Menu API. Since we already created the query in the IDE – this will be easy.

First import graphql and useStaticQuery from gatsby

import React from "react"
import { graphql, useStaticQuery } from "gatsby"

const Header = () => {
...

Next setup the query function at the first line in the Header component.

...
const Header = ({ siteTitle }) => {
  const data = useStaticQuery(
    graphql`
      {
       // our query will go here
      }
      
    `
  )
...

Finally copy the query we built in the IDE at http://localhost:8000/___graphql into the query statement – the final result should look like this:

...
const Header = ({ siteTitle }) => {
  const data = useStaticQuery(
    graphql`
      {
        wpMenu {
          id
          menuItems {
            nodes {
              parentId
              label
              path
              childItems {
                nodes {
                  path
                  label
                }
              }
            }
          }
        }
      }
    `
  )

return (
...

Re-run gatsby develop and console.log(data) to test the query.

Houston, we have a problem.

You might have noticed that there is an issue with our query. Currently we are getting all the menu items in the menu – regardless if they are nested or not.

To fix the issue – we will want to filter out the children by checking if a menu item has a parentID.

Currently our query is returning duplicate menu items – “WordPress” is child of Tutorials as well as its own menu item below.

Filter out the children

We dont want nested menu items passed to the Menu component becuase it will result in duplicates and not reflect how the menu was build in the admin section (the entire point of this post).

Luckily modern Javascript makes it quite easy to filter an array with filter.

Add this line after the query:

...

  // filter only parents
  const menu = data.wpMenu.menuItems.nodes
    ? data.wpMenu.menuItems.nodes.filter(menuItem => menuItem.parentId === null)
    : null

   return(
...

Now we are checking to see if there are any menu items, if yes filter out the top-level menu items – ELSE – return null.

Console.log(menu) and you should only see the top-level menu items.

Console.log(menu) returns a filtered array of top-level menu items.

Finally – we can pass our data to a menu component which will take care of all the list-item logic.

Quickly create a Menu component at src/components/menu.js

// src/components/menu.js

import React from "react"

const Menu = ({menu}) => <p>im gonna be a menu one day</p>

export default Menu

Import your menu and pass it your filtered array of menu items.

import { graphql, useStaticQuery } from "gatsby"
import React from "react"
import Menu from "./menu"

const Header = ({ siteTitle }) => {
...

return (
    <header className="header">
        <h1>Gatsby + WordPress Menu's<h1>
        <Menu menu={menu} />
    </header>
  )

}

Finally we are ready to create a Menu component which will handle all the logic of displaying menu items.

Building the Menu component

The menu component will be responsible for listing our menu items – it will loop each menu item and check if it has any children.

If there are no children menu items – we will display the current menu item as a standard link.

If there ARE children – we will display it as a dropdown menu item which will show the children on hover.

Setup the menu – loop the items

First we will setup the menu, loop through each item in the menu and check if it has any children.

// src/components/menu.js

import React from "react"

const Menu = ({menu}) => (
  <ul className="menu">
  // this is where we will do the loop
  </ul>
)

export default Menu

Next we will loop through each item in the menu and check if it has children

...
const Menu = ({menu}) => (
  <ul className="menu">
    {menu.map(menuItem => {
      const children = menuItem.childItems.nodes.length
        ? menuItem.childItems.nodes
        : null

      return children ? (
        <DropdownMenuItem parent={menuItem} children={children} />
      ) : (
        <MenuItem menuItem={menuItem} />
      )
    })}
  </ul>
)
...

Above we are looping the menu and checking if it has children – if the item has children we display a dropdown menu ELSE display a normal menu.

MenuItem

First we will define the menu item because its simple – add the following below the Menu component.

...
const MenuItem = ({ menuItem }) => (
  <li className="menu-item">
    <a href={menuItem.path}>{menuItem.label}</a>
  </li>
)

The menu item is passed a menuItem in its props – it simple returns a list item with a link and label.

DropdownMenuItem

The DropdownMenuItem is a bit more interesting – it uses a bit of state logic to decide whether or not to show its children.

const DropdownMenuItem = ({ parent, children }) => {
  const [show, setShow] = useState(false)

  return (
    <li
      className={`dropdown menu-item`}
      onMouseEnter={() => setShow(true)}
      onMouseLeave={() => setShow(false)}
    >
      {parent.label}
      <div style={{ display: show ? "block" : "none" }}>
        <div className="flex-square">
          {children.map(child => (
            <Link to={child.path}>{child.label}</Link>
          ))}
        </div>
      </div>
    </li>
  )
}

Lets try to understand this component bit-by-bit.

const DropdownMenuItem = ({ parent, children }) => {

The DropdownMenuItem is passed the current menu item as well as its children via props.

const [show, setShow] = useState(false)

The component also uses useState – which simply returns a variable set to false and a function for setting that variable.

return (
    <li
      className={`dropdown menu-item`}
      onMouseEnter={() => setShow(true)}
      onMouseLeave={() => setShow(false)}
    >

Next we create a <li> element, give it the ‘dropdown’ and ‘menu-item’ classes and setup some events.

Essentially when we hover the <li> show is set to true – when we are not hovering show is set to false.

...            
      {parent.label}
      <div style={{ display: show ? "block" : "none" }}>
        {children.map(child => (
          <Link to={child.path}>{child.label}</Link>
        ))}
      </div>
    </li>
  )
}

export default Menu

At {parent.label} we are adding the text of the parent menu item.

<div style={{ display: show ? "block" : "none" }}>

Then we conditionally edit the wrapper’s display property to ‘block’ if show is true ELSE none

...         
        {children.map(child => (
          <Link to={child.path}>{child.label}</Link>
        ))}
...

Finally loop the children nodes and create a link to each of them.

You did it!

Good job – we setup our Gatsby site to query our existing WordPress site for the menu.

Hopefully this gives you an idea how you can deploy new Gatsby applications while keeping the robust and familiar WordPress admin section.