Now that we have our react app setup, let’s form how our application will be structured. We’re going to need 3 main folders: 1) templates, 2) partials and 3) context. These are all going to be inside react-src/src folder.
The templates folder is where we’re going to store the main files template files – namely Single.js and Archive.js. Single shows a single post, while Archive shows a running list of post in reverse chronological order.
The partials contain bits and pieces of reusable parts. These are files that make up the header, navigaition, pagination and the loop.
The context is where the data sits. We’re going to have two files in here – Context.js and WithConsumer.js. These files are what we need to store state and props. More on this later.
Also, don’t forget that we have our public folder (which is outside the src directory). This contains all the regular PHP files such as functions.php etc.
Routes
Let’s begin by adding a simple route. Make sure we’re in development mode by running:
npm run wpstart
Let’s create a new component called “Single”. Inside it, let’s start simple and put in the code below:
import React from 'react';
const Single = (props) => {
return (
<div className="Post">
this is the page component
</div>
)
}
export default Single;
Pretty basic right? Now let’s install react router by running the command below:
npm install react-router-dom
Open up index.js and include react router in it
import { BrowserRouter, Switch, Route } from 'react-router-dom';
Inside the render function, let’s modify it to look like this:
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route path='/page' component={Single}/>
</Switch>
</BrowserRouter>, document.getElementById('root'));
Now navigate to your site, but append the path “/page”. You should see your page component rendered.
Now let’s go back to index.js and modify the Route to look like below:
<Route path="/page/:slug" component={Single} />
Notice we’re adding a :slug to the path. We’re going to be updating our component with this parameter. The slug will be the actual “slug” of the WordPress post. If you modify our page component with:
<h1>this is the slug: {props.match.params.slug}</h1>
You should see something like below:
So by now, you should see how we’re going to continue with our theme. We’re going to be adding the following routes:
- Home – mapped to “Archive”
- Page + slug – mapped to “Single”
- Post + slug – mapped to “Single”
- Search + term – mapped to “Archive”
- 404 – mapped to “404”
Go ahead and copy the Single component, duplicate it – and call it “Archive”. Then let’s match it to the routes above.
Our index.js now looks like below:
import React from 'react';
import ReactDOM from 'react-dom';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom'
import './index.css';
import Archive from './templates/Archive';
import Single from './templates/Single';
import Notfound from './templates/Notfound';
const routes = (
<Router>
<Switch>
<Route exact path="/" component={Archive} />
<Route path="/page/:slug" component={Single} />
<Route path="/post/:slug" component={Single} />
<Route path="/search/:term" component={Archive} />
<Route component={Notfound} />
</Switch>
</Router>
)
ReactDOM.render(routes, document.getElementById('root'));
Partials
Let’s quickly talk about our partials. Partials are components that we will be reusing in each route component. Things that come to mind are header, footer, sidebar – and whichever reusable piece of code we will come across along the way.
Let’s go ahead and create 2 partials. Head.js and Foot.js. These will be the header and footer of our pages. The code will be a simple functional component like below:
/******Head.js*************/
import React from 'react'
function Head(){
return (<div className="header">header</div>)
}
export default Head;
/******Foot.js*************/
import React from 'react'
function Foot(){
return (<div className="footer">footer</div>)
}
export default Foot;
Now in our template components, simply import, and add the components in. Here is the how our Single.js will look like with the partials. Note – make sure you add them inside the main div:
import React from 'react'
import Head from '../partials/Head';
import Foot from '../partials/Foot';
const Single = (props) => {
return (
<div className="Post">
<Head></Head>
<h1>Home</h1>
<Foot></Foot>
</div>
)
}
export default Single
In the browser, you will see our partials and home page starting to take shape. Inspect the code and it should look like below:
Do the same thing to the rest of our main components so they all share the same header and footer.
Context (Data)
As mentioned above, this is where our data will sit. React has Context API which allows you to have a centralized data store – so that you don’t have to pass data through each child component. This article is probably the best explanation on Context API. As a matter of fact, the two files that we’re going to build, is code that came from the article.
Let’s start with Context.js. This file is considered to be the “Provider“. Add the code below:
import React from "react";
const storeContext = React.createContext();
export const Consumer = storeContext.Consumer;
export class Provider extends React.Component {
constructor(props) {
super(props);
this.state = {
posts : []
};
}
componentDidMount(){
this.setState({
posts : ['title','title2']
})
}
componentDidUpdate(prevProps){
//more code here later...
}
render() {
return (
<storeContext.Provider value={this.state}>
{this.props.children}
</storeContext.Provider>
);
}
}
Don’t worry so much about the test posts data. We’re just using that for – well, testing. Then let’s create WithConsumer.js
import React from "react";
import { Consumer } from "./Context";
function WithConsumer(WrappedComponent) {
return function(props) {
return (
<Consumer>
{ctx => <WrappedComponent {...props} context={ctx} />}
</Consumer>
);
};
}
export default WithConsumer;
Now this one is pretty confusing. It’s basically a wrapper – for our components that require the “Consumer” component. Basically, all our components that want to “consume” data from our “Provider” will be using this file.
Now let’s create a partial called “TheLoop“. Add the code below:
import React from 'react';
import WithConsumer from '../context/WithConsumer';
const TheLoop = ({ context }) => {
const posts = () => context.posts;
const pos = posts();
return (
pos.map(function(item,i){
return <div key={i}>{item}</div>
})
);
};
export default WithConsumer(TheLoop);
Notice that in our export – we’re passing our component TheLoop as a parameter to WithConsumer.
We’re going to talk more in detail about TheLoop component in our next session. For now, we just want to get it up and running – to demonstrate Context and Consumer.
Let’s open Single.js from our templates folder and modify it to look like this:
import React from 'react';
import Head from '../partials/Head';
import TheLoop from '../partials/TheLoop';
import Foot from '../partials/Foot';
import {Provider} from '../context/Context'
const Single = (props) => {
return (
<Provider>
<div className="Post">
<Head></Head>
<TheLoop></TheLoop>
<Foot></Foot>
</div>
</Provider>
)
}
export default Single
You see how we’re wrapping everything in “<Provider>“, this is so we have access to our Context.js data. Also, we’ve included “TheLoop” as a component in between Head and Foot.
You should see “title” and “title2” in the browser by now.
Lastly, we need a way to get our “slug” parameter into our context. We can use props for that. Still in Single.js, pass the slug via props like so:
<Provider router={props}>
In Context.js, we should have access to the slug – through “props.router.match.params.slug“. As a matter of fact, we have access to the entire router object here, in a prop called “router”.
Let’s stop here for now. We are going to discuss more about TheLoop and Axios in our next session.
I have copied the code above exactly and for some reason react router dom does not seem to work.I have even copied and pasted the code onto another react project that is not on wordpress and the router works, so this makes me think there is something else that needs to be set up for it to work properly with wordpress. Perhaps something to do with permalinks or .htaccess?
In detail, it would seem that path=”/” or no specified path work, but I cannot use “exact” or have any path that is more than “/”, therefore “/path” doesn’t work.
Is there something I have missed out to get this to work in terms of setup? Please help!
I would try single route first to see if it works – without the “exact”. I may have left a few things out above for the sake of keeping the tutorial short. The entire code can be seen here: https://github.com/michaelsoriano/barebones/tree/master/ for reference.
Hi there,
Thanks for the response, but yes this was all without the “exact” attribute initially, its just its literally anything that is more than “/” and even the exact doesnt work, so literally nothing works.
I will look through your code to see if im missing anything
I too faced the same issue..
Spent 3 days trying to figure it out, till I saw this comment.
Would be nice if his could be sorted.
I had the same issue, router was not working at all, no matter what rules. I checked documentation:
https://reacttraining.com/react-router/web/api/BrowserRouter
The issue was that I run my project on localhost/project/ link, but router was checking only localhost/ link by defined rules, and none of them worked. I just rendered About to confirm it, and got localhost/about link, not localhost/project/about as I would like to get.
And Solution what worked for me was add basename to BrowserRouter or Router whatever naming you’re using –
Hi,
Am stuck on the same issue as well, anyone got a workaround? Navigating to localhost/page gives a 404 error.
Same problem! Did you fix that?
Amending the path on index.js solved the problem for me for now…
Amending the path solved the problem for me …..Route path=’/react/page’ component={Single}/>
Thanks for the resp, but wont work for me 🙁
What finally solved it for me is @Thomas’ solution above
this solve the problem for me also
i use two way for this problem and both way is working :
1.
<Route path="/page" element={} />
2.
<Route path="wp-react/page" element={} />
my code not completely display
I am not sure if this anyone is still having issues with the React Router portion of the tutorial. Try switching the code up a little, like so:
*/wp/ = Direct Route Path
From:
To:
<Route path="/wp/page" element={} />
Do this in addition to
show your code
Thanks for the excellent series Michael! For what it’s worth, I threw together a quick component that that uses WP’s built in slug for pages which avoided the need to prefix existing urls. https://gist.github.com/quartarian/e4a022978e447e3a4980922e38c3dda1.
Thank you Matt.
This is exactly what I’ve been looking for. When will the complete walkthrough be posted to your site?
Yes – I’ve been too busy these days working on another project. The theme is done, but the tutorial I will finish it soon. Sorry about that.
The theme, is it using server side rendering (SSR) or client rendered?
React is using client. But you can still use PHP – for WordPress related stuff.
how to use ssr with this theme ? please help me
I think WP REST API is the best way for react theme to fetch server-side data.
https://developer.wordpress.org/rest-api/
As soon as I created the folders “templates”,”partials”,”context” in the barebones/react-src. and run the command “npm run wpstart”
Everything stops working.
Is there an error? Is it just a blank page? If it is I’ve had that happen many times.
It says Single not defined
The reason is, you need also to add this:
import Single from ‘./templates/Single’;
to your index.js file, and put all the folders inside react-src/src folder, not react-src
Hope that helps 😉
And Michael, please fix that in the article if possible.
P.S. Thanks a lot for all those tutorial 🙂
Thank you Anton. Updated the article.
I did all like in examples, was couple mistakes in code, but after couple of hours i fixed it, but still was the problem when I try to create post with permalink example.com/post/some-new-post and I fixed it with plugin Permalink Manager Lite. Maybe for someone will be useful my comment. Thank you Michael for article.
Nice tutorial, thanks so much
Hi,
to use the function.php (for example to add custom image size) i’ve created a file named function.php in barebones/react-src/ with specific command.
After that i noticed that this file was copied in barebones root.
But this file do no work inside wordpress and seems to ignore. How i can set this php file in react js theme?
thank you
Add the functions.php inside the barebones/react-src/public folder. When you’re in dev mode – this should be replicated and copied to root.
Warning: require(C:\xampp\htdocs\mortgage/wp-content/themes/barebones/inc/template-tags.php): failed to open stream: No such file or directory in C:\xampp\htdocs\mortgage\wp-content\themes\barebones\functions.php on line 154
give me error like this after add function.php file inside of public folder
Routing here depends on your Apache config. For me, I had to google a lot to get an url rewriting rule that works (on macos).
Something similar might be required on a new setup.
“`
#
# Possible values for the Options directive are “None”, “All”,
# or any combination of:
# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
#
# Note that “MultiViews” must be named *explicitly* — “Options All”
# doesn’t give it to you.
#
# The Options directive is both complicated and important. Please see
# http://httpd.apache.org/docs/2.4/mod/core.html#options
# for more information.
#
Options FollowSymLinks Multiviews
MultiviewsMatch Any
#
# AllowOverride controls what directives may be placed in .htaccess files.
# It can be “All”, “None”, or any combination of the keywords:
# AllowOverride FileInfo AuthConfig Limit
#
AllowOverride All
#
# Controls who can get stuff from this server.
#
Require all granted
# BEGIN WordPress
RewriteEngine On
RewriteBase /
RewriteRule ^index.php$ – [L]
# Include in the next line all folders to exclude
RewriteCond %{REQUEST_URI} !(mobile) [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
“`
Thank you Thomas
Thanks @Thomas, this is what eventually solved it for me.
If it seems like the router is not working, check permissions on the .htaccess file. I had to check the admin > settings > permalink and noticed the notification at the very bottom about issues with the .htaccess. Once i solved it, i was able to continue the tutorial. The router started displaying the “this is the page component” message for the path /page.
Thank you vs-effect.
How did you solve it exactly? I have the same issue but can’t figure out how to change the default WP settings.
simply changing permalink to post name solved the issue for now.
I have the same issue with the React Router, any component outside of the root ‘/’ returns a 404 error.
I suspect it is something to do with the request reaching the server (WordPress) before the React app, which makes WordPress to automatically load its own 404 page instead of the index.php with the react code. I haven’t figured out how to solve this issue either but it’s a blocker to complete the other tasks.
The code in the tutorial has a few typos and issues too that make it hard to follow unless you spot and fix them as you go. For example, you are creating a const ‘Single’ and exporting it as ‘Home’.
Seems like editing the .htaccess file (hidden in the root directory of the WP website) fixed the issue.
Just needed to add this code between BEGIN WordPress and END WordPress:
` # BEGIN WordPress
RewriteEngine on
# Don’t rewrite files or directories
RewriteRule ^index\.php$ – [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index\.php [L]
# END WordPress `
Not sure if this will cause issues once in production, since it won’t be loaded in the localhost. But at least it allows the router to work in the dev env.
Thank you Jake B.
I’m using a local by flywheel and a remote WP installation. In both installation I can see the Page Component but in console I got some errors:
* 404 on page route
* Uncaught (in promise) bad-precaching-response: bad-precaching-response :: [{“url”:”https://…}]
I tried to modify my .htaccess with Thomas solution but I got a 500 Error Server. Any idea how to fix?
Thanks
I’m having the same exact problem. Did you ever figure this out?
Likely a formatting issue but most of the divs in the example code are represented as < and >. Definitely don’t want to copy and paste this code.
Yes – I have a problem with the code syntax in the articles. It doesn’t represent JSX very well. Don’t copy and paste.
Thanks.
I tried console.log and notice that TheLoop was called twice. How is that possile?
Hi, I am new to react as I am a WordPress Dev.. When I got to the step to “install react-router-dom”- I got the following npm warn messages:
[deleted error messages…]
Like I said, I am new to React and am trying to learn how to develop my own react themes for wordpress, so I was delighted when I found your create-react-wptheme on Michael’s blog!
I guess my questions are;
1.) Is this a major ordeal or can I just continue to develop a wp theme with this package and not worry about those warning messages?
OR
2.) How can I fix these dependency vulnerabilities on my own?
I have also added this as an issue to the package on git, as I am unsure what the correct way to go about fixing this issue, so any insight would be greatly appreciated!
I had to remove the error messages from your comment above – because it was causing display issues. But – yes it would be good if warnings / errors are gone. Although I find that during a lot of npm installs, I cannot avoid it. Even with audit fix etc. I sometimes just want to move forward so I just continue developing.
I am using a newer create-react-wptheme and react-router-dom on a new project and I didn’t run into any issues.
Yes – add an issue to the git. I think they might lead to better answers than me.
If you have this problem – 404 on page route
Use this code:
import React from ‘react’;
import ReactDOM from ‘react-dom’;
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from “react-router-dom”;
import ‘./index.css’;
import Single from ‘./templates/Single’;
import * as serviceWorker from ‘./serviceWorker’;
ReactDOM.render(
Page
, document.getElementById(‘root’));
ReactDOM.render(
Page
, document.getElementById(‘root’));
Why are all your opening div tags showing up as “<” in your post? You might want to update.
Yes = its a bug in the syntax highlighter plugin. It’s so annoying. Thanks for letting me now.
I am getting and blank screen after adding single.js and editing index.js, i have also added import Single from ‘./templates/Single’; but still facing same issue. i have gone through every step from this article and implemented but still showing blank white screen. no issue while compiling. Pleases help me ASAP
i’ve had the exact same problem. I even downloaded the entire repository from his github and still can’t get it working.
Hello .!
function.php file is not working inside of template folder . please help me