Using WordPress Permalinks in a Custom WordPress React Theme

Part 4 in a series on Creating a React WordPress theme, In this article we look into updating our React Router to work with WordPress permalinks and display post type and taxonomy archives and single post pages.

This post is part of the React WordPress Theme Collection

An Step-by-step guide on creating a React WordPress theme, to list WordPress Rest API posts and pages, display and create comments, integrate Server Side rendering using PHP.

  1. Create a React WordPress theme using babel and webpack
  2. Step-by-step Guide to displaying WordPress Rest API posts in a React WordPress theme
  3. Guide to Creating React WordPress menus in a WordPress Theme
  4. Using WordPress Permalinks in a Custom WordPress React Theme

Up until now our React WordPress theme has only been able to list and view single posts, in this article we will be overhauling the routing system to work with all taxonomies and custom post types.

The best way i have found so far to work with taxonomies and custom post types is to generate a list of all permalinks within wordpress and pass these to our React WordPress theme using props.

Generate list of wordpress routes

We use the get_post_type_archive_link to get the archive permalink for each post type, along with the post type that is being linked, and setting it to use the Archive react component.

/**
 * Generate list of post type archive routes
 *
 * @return array
 */
function jcrt_generate_post_type_archive_routes()
{
    $routes = [];

    $post_types = get_post_types(array(
        'public' => true
    ));

    foreach ($post_types as $post_type) {

        $link = get_post_type_archive_link($post_type);
        if (!$link) {
            continue;
        }

        $link = '/' . jcrt_strip_site_url($link);
        $routes[$link] = ['component' => 'Archive', 'post_type' => $post_type];
    }

    return $routes;
}

We then list all single post type permalinks by looping through each post type listing all and fetching the single post_types url using get_permalink.

/**
 * Generate list of post type routes
 *
 * @return array
 */
function jcrt_generate_post_type_routes()
{
    $routes = [];

    $post_types = get_post_types(array(
        'public' => true
    ));

    foreach ($post_types as $post_type) {

        $query = new WP_Query(array(
            'post_type' => $post_type,
            'posts_per_page' => -1,
            'fields' => 'ids'
        ));

        if (!empty($query->posts)) {
            foreach ($query->posts as $id) {
                $url = get_permalink($id);
                $url = '/' . jcrt_strip_site_url($url);
                $routes[$url] = ['component' => 'Single', 'post_type' => $post_type, 'id' => $id];
            }
        }
    }

    return $routes;
}

Finally we loop through all taxonomy term archives and return which post types belong to each.

/**
 * Generate list of taxonomy archive routes
 *
 * @return array
 */
function jcrt_generate_taxonomy_archive_routes()
{
    $routes = [];

    /**
     * @var WP_Taxonomy[] $taxonomies
     */
    $taxonomies = get_taxonomies(array(
        'public' => true,
        'show_in_rest' => true
    ),  'objects');

    foreach ($taxonomies as $taxonomy) {
        $terms = get_terms(array(
            'taxonomy' => $taxonomy->name,
            'hide_empty' => false
        ));
        if (!empty($terms)) {
            foreach ($terms as $term) {

                /**
                 * @var WP_Term $term
                 */
                $link = '/' . jcrt_strip_site_url(get_term_link($term, $taxonomy));
                $routes[$link] = ['component' => 'Archive', 'post_type' => $taxonomy->object_type, 'tax' => $taxonomy->name];
            }
        }
    }

    return $routes;
}

Combining all previous route generation functions we merge the results together.

/**
 * Generate list of all wordpress routes
 *
 * @return array
 */
function jcrt_generate_routes()
{
    $routes = [];
    $routes = array_merge($routes, jcrt_generate_post_type_routes());
    $routes = array_merge($routes, jcrt_generate_taxonomy_archive_routes());
    $routes = array_merge($routes, jcrt_generate_post_type_archive_routes());
    return $routes;
}

Add wordpress routes to React Router

With a full list of WordPress single and archive permalinks we need to pass these into our react app via props, we do this by updating our $config array that is accessible via the React theme, using the previously created function jcrt_generate_routes.

$config = array(
    //...
    'routes' => jcrt_generate_routes()
);

Pass the config routes variable to our React WordPress themes main App Component via the routes props.

const { routes, menu } = window.wp_config;

ReactDOM.render(
  <BrowserRouter>
    <App routes={routes} menu={menu} />
  </BrowserRouter>,
  document.getElementById('root')
);

With the routes accessible via our App Component we loop through each route within our React Router Switch Component loading either the Archive or Single component passing the post_type to each, and also the id to the Single Component.


  {Object.keys(routes).map((route) => (
    <Route key={route} path={route} exact={true}>
      {routes[route].component === 'Archive' && (
        <Archive post_type={routes[route].post_type} />
      )}
      {routes[route].component === 'Single' && (
        <Single
          id={parseInt(routes[route].id)}
          post_type={routes[route].post_type}
        />
      )}
    </Route>
  ))}
  <Route>
    <h1>The Page you are looking for cannot be found.</h1>
  </Route>
</Switch>

With the post type being passed to the Archive Component, we need to update the rest api endpoint being used when fetching the list of posts, if its page we set the endpoint to pages, and the same for it its post we also set the endpoint to posts, otherwise we set the endpoint to the custom post type name.

const { post_type } = this.props;

let endpoint = post_type;
if (post_type === 'page') {
  endpoint = 'pages';
} else if (post_type === 'post') {
  endpoint = 'posts';
}

return Axios.get(
  window.wp_config.rest_url + 'wp/v2/' + endpoint + '?page=' + page
)

We repeat this on the single component to select the correct endpoint.

const { post_type } = this.props;

let endpoint = post_type;
if (post_type === 'page') {
  endpoint = 'pages';
} else if (post_type === 'post') {
  endpoint = 'posts';
}

Axios.get(window.wp_config.rest_url + 'wp/v2/' + endpoint + '/' + id)

With these simple steps post, page and custom post type and custom taxonomy archive and single view is visible within our react WordPress theme.

Leave a Reply

Fields marked with an * are required to post a comment