Convert WordPress to static using Astro

WP-Owls-article-cover-12

Astro is a new Static Site Generator, that focuses on shipping less front-end JavaScript. It’s also very simple to use and connect with different APIs. That’s why in this tutorial I’m going to show how to connect WordPress REST API together with Astro.

Advertisment
LMS created by e-learning experts

Installation

Astro installation is very simple. First, we have to run:

npm init astro

and than:

npm install
npm start

We can also use specific templates during the init phase by using the --template parameter. You can read more about it in the official documentation.

.env file

Astro has a built-in way of handling variables set in a .env file.

Just create a .env file, place your variables inside like this:

PUBLIC_API_URL = https://domain.com/wp-json/wp/v2

The only tricky part is to remember to start them with PUBLIC_ .

Then we can use them anywhere in our code with something like this:

import.meta.env.PUBLIC_API_URL

And that’s it 🙂 You don’t have to install any additional library or anything.

WordPress API

As I mentioned – we’re going to use the REST API. That’s why we’re going to create some helper functions that will make everything simpler.

const API_URL = import.meta.env.PUBLIC_API_URL;

export async function fetchAPI( query='' ) {
    const res = await fetch( `${API_URL}/${query}` );

    if ( res.ok ) {
        return res.json();
    } else {
        const error = await res.json();

        throw new Error(
            '❗ Failed to fetch API for ' + query + "\n" +
            'Code: ' + error.code + "\n" +
            'Message: ' + error.message + "\n"
        );
    }
}

export async function getArticles() {
    const data = await fetchAPI( 'articles/?per_page=50' );

    return data;
}

The fetchAPI returns either the data from the API or an error. We can also pass the $query parameter.

The second function is getArticles – it’s just a helper, so we don’t have to write fetchAPI('articles/?per_page=50') each time.

Layout and components

Because we don’t want to repeat ourselves with writing some parts of our code all the time, we are going to create a template first.

Let’s create a src/layouts/Base.astro :

---
import Header from '../components/Header.astro'
const {pageTitle = 'Hello world'} = Astro.props
---
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width">
	<title>{pageTitle}</title>
	<link
      rel="stylesheet"
      type="text/css"
      href={Astro.resolve( '../styles/global.css' )} />
</head>
<body>
	<div>
		<Header/>
        	<slot />
	</div>
</body>
</html>

The most important here is the <slot/> component. Every time we will use <Base>some content</Base> the <slot/> component will be replaced with “Some content”.

We also used a Header component here:

<nav class="text-center p-6 mb-6 bg-green-500 shadow-lg">
  <a href="/" class="cursor-pointer p-4 ml-2 text-white">WP Owls</a>
</nav>

Let’s also create an Article component, that we will use on all the archive pages:

---
const { post } = Astro.props
---
<div>
  <h2>{post.title.rendered}</h2>
  <a href={`/` + post.slug}>Read more</a>
</div>

With these components, we have all the foundations prepared.

Structure

Because Astro has a file-based routing we will have to create a few pages first:

pages
-- page
---- [...page].astro
-- [slug].astro
-- index.astro

the page/[page].astro will be used for pagination, the [slug].astro will be used to generate single articles and the index.astro is the homepage.

Fetching articles

Let’s start with the homepage. It’s time to edit pages/index.astro

---
import {fetchAPI} from '../lib/api.js';
import Article from '../components/Article.astro';
import Base from '../layouts/Base.astro';

const data = await fetchAPI('articles?per_page=4');
---
<Base>
    <article class="prose lg:prose-xl mx-auto">
    <h1>Articles archive</h1>
    <div>
        <ul>
            {data.map(post => <li><Article post={post}/></li>)}
        </ul>
    </div>
     <div class="text-center mt-20">
        <div>
            <a href="/page/" class="w-full bg-blue-500 px-4 py-3 rounded text-white font-semibold hover:bg-blue-600 transition duration-200 each-in-out">See more articles</a>
        </div>
    </div>
    </article>
</Base>

It’s quite obvious what we are doing here – we are fetching the latest 4 articles and there is also a button that links to the full archive.

We also used the <Article /> . As you can see – it has the post props, thanks to which we can pass the post data.

Generating single pages

Time to open the [slug].astro file:

---
import Base from '../layouts/Base.astro';
import {getArticles} from '../lib/api.js';

export async function getStaticPaths() {
  const data = await getArticles();
  return data.map((post) => {
    return {
      params: { slug: post.slug },
      props: { post } };
  });
}
const {post} = Astro.props;
---
<Base>
  <article class="prose lg:prose-xl mx-auto">
    <h1>{post.title.rendered}</h1>
    <div>
        {post.content.rendered}
    </div>
  </article>
</Base>

The most important thing that happens here is using the getStaticPaths function. It lets us create single pages based on some dynamic data. In this case, we are traversing through all the fetched data and returning the object with params (that we are using as the page slug) and props (additional data that we will use during the page generation).

Pagination

In the end, we have to open the page/[...page].astro :

---
import {getArticles} from '../../lib/api.js';
import Article from '../../components/Article.astro';
import Base from '../../layouts/Base.astro';

export async function getStaticPaths( { paginate } ) {
  const data = await getArticles();
  return paginate(data, { pageSize: 4 });
}
const {page} = Astro.props;
---
<Base>
    <article class="prose lg:prose-xl mx-auto">
      <h1>Articles archive</h1>
      <div>
          <ul>
              {page.data.map(post => <li><Article post={post}/></li>)}
          </ul>
      </div>

      {page.url.prev && <a href={page.url.prev}>Previous page</a>}
      {page.url.next && <a href={page.url.next}>Next page</a>}
    </article>
</Base>

This time in the getStaticPaths function we are using the paginate function. It has two parameters data and a number of posts per page.

You can find all the code in this repository.

Advertisment
LMS created by e-learning experts

Wrapping up

We just scratched the surface here, but it should help you start your adventure with Astro and WordPress. Next, you can try adding more Custom Post Types and adding the navigation.

As you see – Astro is very straightforward to use and connecting it with WP is very simple.

Further reading

If you like this article, don't forget to share it.