Headless WordPress is an idea that is being used more and more. I am going to try and cover a few topics that might be interesting when working with Headless WordPress setup. In this tutorial, we are going to see how we can login a user using JWT.

We are going to make a simple App that will enable the user to login. Once logged in, we will show the user their information.

To be able to login with JWT, you’ll need to install and activate the plugin JWT Authentication for WP REST API.

Preparing our Application

Let’s now create our own React App with the Create React App. Open your terminal (command prompt) and place yourself in a folder where you develop sites or where you want this app to reside.

Then use this command:

npx create-react-app my-app

You can replace my-app with your own choice. This will be your folder where all the code will be. I’ve named my own headless.

Then, place yourself in that folder, you can then start the app with npm start. Before we do that, we will also install Gutenberg Components, the npm package @wordpress/components. This will enable us to use those components in our app.

You can install them with this command:

npm install @wordpress/components

Since Gutenberg components do not provide us with layout classes, we could create our own layout CSS guide or we can use existing frameworks so we don’t waste our time on that. If you’re building an app that will be used in production, then rethink this and you might want to build your own CSS layout 🙂

We will use Bootstrap 4 for that and we can also install that using this command:

npm install bootstrap

You will also need to process SASS (SCSS) so we need to have that here so our app can process such files. To have that installed, you need to use this command:

npm install node-sass --save

As we will use the Bootstrap and Gutenberg styles, we can delete the App.css and create App.scss. Inside of this new file, put this:

@import '~bootstrap/scss/bootstrap.scss';
@import '~@wordpress/components/build-style/style.css';

This will now compile into a CSS file that includes both Boostrap styles and also Gutenberg styles.

Since we will have to peform POST and GET requests to our WordPress site, we need to have a way to do that. You can use the browser API fetch or a library. We will use the axios library. Install it with this command:

npm install axios

Adding Login Logic to our Headless WordPress

Open the App.js so we can start adding our login logic there. We will check if the user is logged in. If it’s not, we will show the login form. Otherwise, a dashboard will be shown.

Let’s change the imports of the App.

Don’t worry about errors for now. Components Dashboard and Login don’t exist yet so you’ll get compile errors.

You can now replace the whole App function with this:

So, what are we doing here?

  • We are using React Hooks useState to handle the login information globally,
  • We are using also the Effect Hooks useEffect to handle the change on the login part,
  • If we are logged in, we will show the Dashboard component here,
  • If we are not logged in, we will show the Login Component.

With the useEffect, our app will login our user (if we have the token saved) once the App is rendered.

We pass the setLogin function, that controls the global state for login information, to both components, so we can sign the user or even logout.

Creating the Login Component

To login a user with our Headless WordPress application, we will need to pass the username and password on the first try. To do that, we need a form. Create the folder components in your app folder and create a Login.js file.

Put this code in there:

We are importing the Gutenberg components TextControl and Button that we will use to create the login form. We will also have 3 more methods. Those methods will be used to store the information into the state and also to login the user.

Let’s now create the render method and the 2 methods to save the information into the state. You’ll now see how we will use the Gutenberg Components.

The second TextControl is also receiving the type as password. When each control has changed, it will use the appropriate method to store the information into the state.

When we click on the Button component, we will try to login the user. Let’s create that method now.

We are posting the username and password to the REST route for the jwt token. If the user exists, it will return the token to us. We will then set the token to the local storage so we can retrieve it on refresh.

If there are any errors returned from that REST route, we will display it without the HTML tags.

This will now enable our users to login into our WordPress Headless application.

Creating the Dashboard Component

Within our Dashboard component, we will show the information of our user so we know we are getting the correct user in our WordPress Headless application.

Create the file Dashboard.js inside of our folder components and put this into it:

We are also requiring the axios here because we will get the information about the currently logged in user. We want to get that information when the component mounts, so it is available as soon as possible.

We are using the route wp-json/wp/v2/users/me to get information about the current user. When doing that, we are passing the Authorization header with our Bearer token.

If we don’t provide such header, the JWT plugin won’t be able to define which user is logged in.

Let’s now display the information of the logged in user.

We have the button to logout the user that will also use the passed method setLogin. Since we are setting the login to an empty string, the App will re-render and display the login form.

Get the code

When you download the code, you’ll have to install the packages by running npm install.

This part is available only to the members. If you want to become a member and support my work go to this link and subscribe: Become a Member


Headless WordPress is becoming the new standard, slowly. With that, you can create a Web App that can be easily transformed into an Android or iOS app. By using the Gutenberg Components, we can save our time by using useful components.

Become a Sponsor

Posted by Igor Benic

Web Developer who mainly uses WordPress for projects. Working on various project through Codeable & Toptal. Author of several ebooks at https://leanpub.com/u/igorbenic.


  1. Thank you! after spending hours trying to figure out an issue with malformed header, your syntax finally worked.


  2. I really liked your tutorial, unfortunately I couldn’t get it to work because I couldn’t understand what to do with the login-2 and login-3 files. Were they a progression of the original login.js file or separate files?

    It’s so hard to find good tutorials on this subject, thank you for creating this article.


    1. Hi Darrel, that’s correct. You should not focus on file names, I use that to help me structure and partition the code.

      If I don’t state that you should use a different file, then all of it will go in the same file (if JS), so all login-* files are actually one single file. This is just a way for me to explain bit by bit 😀


  3. Thanks Igor,

    How would you prevent “not logged in” users to try and get content via WP rest api endpoints which are by default open to anyone?

    For example via the endpoint: {{base_url}}/wp-json/wp/v2/posts/


    1. You can check this file for filters that you might find useful. Maybe this filter could help you: https://core.trac.wordpress.org/browser/tags/5.4/src/wp-includes/rest-api/class-wp-rest-server.php#L393


      1. Thanks Igor,

        I appreciate your time for assisting me and thanks a lot for your very informative posts and newsletters. I work as a WP developer for 10 years and not every day I meet a developer with such deep WP knowledge.

        The filter looks interesting although I think it is more concern data embedded modification than what I needed but still very valuable.

        Meanwhile I found this filter: https://stackoverflow.com/a/55157031 that works really nice.


Leave a reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.