├── .gitignore ├── src ├── scripts │ ├── flux │ │ ├── alt │ │ │ └── alt.js │ │ ├── stores │ │ │ └── DataStore.js │ │ └── actions │ │ │ └── DataActions.js │ ├── components │ │ ├── Home.js │ │ ├── About.js │ │ ├── Contact.js │ │ └── Header.js │ └── index.js └── index.html ├── README.md ├── package.json ├── webpack.production.js └── webpack.dev.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /src/scripts/flux/alt/alt.js: -------------------------------------------------------------------------------- 1 | import Alt from 'alt'; 2 | const alt = new Alt(); 3 | 4 | export default alt; -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | React with Wordpress API 5 | 6 | 7 |
8 | 9 | -------------------------------------------------------------------------------- /src/scripts/components/Home.js: -------------------------------------------------------------------------------- 1 | import DataStore from 'flux/stores/DataStore.js' 2 | 3 | class Home extends React.Component { 4 | render() { 5 | let pageData = DataStore.getPageBySlug('home'); 6 | 7 | return ( 8 |
9 |

Homepage template

10 |

{pageData.title.rendered}

11 | 12 |
13 |
{pageData.acf.text}
14 |
15 | ); 16 | } 17 | } 18 | 19 | export default Home; 20 | -------------------------------------------------------------------------------- /src/scripts/components/About.js: -------------------------------------------------------------------------------- 1 | import DataStore from 'flux/stores/DataStore.js' 2 | 3 | class About extends React.Component { 4 | render() { 5 | let pageData = DataStore.getPageBySlug('about'); 6 | 7 | return ( 8 |
9 |

About page template

10 |

{pageData.title.rendered}

11 | 12 |
13 |
{pageData.acf.text}
14 |
15 | ); 16 | } 17 | } 18 | 19 | export default About; 20 | -------------------------------------------------------------------------------- /src/scripts/components/Contact.js: -------------------------------------------------------------------------------- 1 | import DataStore from 'flux/stores/DataStore.js' 2 | 3 | class Contact extends React.Component { 4 | render() { 5 | let pageData = DataStore.getPageBySlug('contact'); 6 | 7 | return ( 8 |
9 |

Contact page template

10 |

{pageData.title.rendered}

11 | 12 |
13 |
{pageData.acf.text}
14 |
15 | ); 16 | } 17 | } 18 | 19 | export default Contact; 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wordpress REST API + React JS 2 | 3 | This boilerplate will help you use React JS with Wordpress REST API. 4 | Clone the project and install node packages: 5 | 6 | ``` 7 | npm i 8 | ``` 9 | 10 | To run the project on localhost:8080: 11 | ``` 12 | npm start 13 | ``` 14 | 15 | To build the project into /build folder: 16 | ``` 17 | npm run build 18 | ``` 19 | 20 | You need to have webpack and webpack-dev-server installed globally: 21 | * [`webpack`](http://webpack.github.io/docs/) 22 | ``` 23 | npm install -g webpack 24 | ``` 25 | 26 | * [`webpack-dev-server`](http://webpack.github.io/docs/webpack-dev-server.html) 27 | ``` 28 | npm install -g webpack-dev-server 29 | ``` 30 | 31 | Don't forget to update the Worpress installation url in DataActions.js. It's located in src/actions/DataActions.js line 7 32 | ``` 33 | class DataActions { 34 | constructor() { 35 | const appUrl = 'http://andreypokrovskiy.com/projects/wp-api'; // Wordpress installation url 36 | } 37 | ``` 38 | -------------------------------------------------------------------------------- /src/scripts/components/Header.js: -------------------------------------------------------------------------------- 1 | import {Link} from 'react-router-dom'; 2 | import DataStore from 'flux/stores/DataStore.js' 3 | 4 | class Header extends React.Component { 5 | 6 | render() { 7 | let allPages = DataStore.getAllPages(); 8 | allPages = _.sortBy(allPages, [function(page) { return page.menu_order; }]); // Sort pages by order 9 | 10 | return ( 11 |
12 | Home 13 | 14 | {allPages.map((page) => { 15 | if(page.slug != 'home'){ 16 | return( 17 | 22 | {page.title.rendered} 23 | 24 | ) 25 | } 26 | })} 27 |
28 | ); 29 | } 30 | } 31 | 32 | export default Header; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wp-api-react", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "webpack-dev-server --inline --progress --config webpack.dev.js", 9 | "build": "npm run clean && webpack -p --progress --config webpack.production.js", 10 | "clean": "rimraf ./build/*" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "alt": "^0.18.6", 16 | "axios": "^0.16.2", 17 | "babel-polyfill": "^6.23.0", 18 | "babel-runtime": "^6.23.0", 19 | "lodash": "^4.17.4", 20 | "react": "^15.5.4", 21 | "react-dom": "^15.5.4", 22 | "react-router-dom": "^4.1.1" 23 | }, 24 | "devDependencies": { 25 | "babel-core": "^6.24.1", 26 | "babel-loader": "^7.0.0", 27 | "babel-plugin-transform-runtime": "^6.23.0", 28 | "babel-preset-es2015": "^6.24.1", 29 | "babel-preset-react": "^6.24.1", 30 | "babel-preset-stage-0": "^6.24.1", 31 | "html-webpack-plugin": "^2.28.0", 32 | "react-hot-loader": "^1.3.1", 33 | "rimraf": "^2.6.1", 34 | "webpack": "^2.6.1", 35 | "webpack-dev-server": "^2.4.5" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/scripts/flux/stores/DataStore.js: -------------------------------------------------------------------------------- 1 | import alt from 'flux/alt/alt.js'; 2 | import DataActions from 'flux/actions/DataActions.js'; 3 | 4 | class DataStore { 5 | constructor() { 6 | this.data = {}; 7 | 8 | this.bindListeners({ 9 | // Listen to the getSuccess() in DataActions.js 10 | handleSuccess: DataActions.GET_SUCCESS 11 | }); 12 | 13 | this.exportPublicMethods({ 14 | getAll: this.getAll, 15 | getAllPages: this.getAllPages, 16 | getAllPosts: this.getAllPosts, 17 | getPageBySlug: this.getPageBySlug 18 | }); 19 | } 20 | 21 | // Store data returned by getSuccess() in DataActions.js 22 | handleSuccess(data) { 23 | this.setState({ data }); 24 | } 25 | 26 | // Returns all pages and posts 27 | getAll() { 28 | return this.getState().data; 29 | } 30 | 31 | // Returns all Pages 32 | getAllPages() { 33 | return this.getState().data.pages; 34 | } 35 | 36 | // Returns all Posts 37 | getAllPosts() { 38 | return this.getState().data.posts; 39 | } 40 | 41 | // Returns a Page by provided slug 42 | getPageBySlug(slug){ 43 | const pages = this.getState().data.pages; 44 | return pages[Object.keys(pages).find((page, i) => { 45 | return pages[page].slug === slug; 46 | })] || {}; 47 | } 48 | 49 | } 50 | 51 | export default alt.createStore(DataStore, 'DataStore'); -------------------------------------------------------------------------------- /src/scripts/index.js: -------------------------------------------------------------------------------- 1 | import {render} from 'react-dom'; 2 | import DataActions from 'flux/actions/DataActions.js'; 3 | 4 | import Home from 'components/Home.js'; 5 | import About from 'components/About.js'; 6 | import Contact from 'components/Contact.js'; 7 | import Header from 'components/Header.js'; 8 | 9 | import { 10 | BrowserRouter as Router, 11 | Route, 12 | Redirect, 13 | Switch 14 | } from 'react-router-dom'; 15 | 16 | 17 | class AppInitializer { 18 | 19 | templates = { 20 | 'about': About, 21 | 'contact': Contact 22 | } 23 | 24 | buildRoutes(data){ 25 | return data.pages.map((page, i) => { 26 | return( 27 | 33 | ) 34 | }) 35 | } 36 | 37 | run() { 38 | DataActions.getPages((response)=>{ 39 | render( 40 | 41 |
42 |
43 | 44 | 45 | 46 | 47 | {this.buildRoutes(response)} 48 | { return }} /> 49 | 50 |
51 |
52 | 53 | , document.getElementById('app') 54 | ); 55 | }); 56 | } 57 | } 58 | 59 | new AppInitializer().run(); 60 | -------------------------------------------------------------------------------- /src/scripts/flux/actions/DataActions.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import alt from 'flux/alt/alt.js'; 3 | 4 | class DataActions { 5 | 6 | constructor() { 7 | const appUrl = 'http://andreypokrovskiy.com/projects/wp-api-react'; // Wordpress installation url 8 | 9 | this.pagesEndPoint = `${appUrl}/wp-json/wp/v2/pages`; // Endpoint for getting Wordpress Pages 10 | this.postsEndPoint = `${appUrl}/wp-json/wp/v2/posts`; // Endpoint for getting Wordpress Posts 11 | } 12 | 13 | // Method for getting data from the provided end point url 14 | api(endPoint) { 15 | return new Promise((resolve, reject) => { 16 | axios.get(endPoint).then((response) => { 17 | resolve(response.data); 18 | }).catch((error) => { 19 | reject(error); 20 | }); 21 | }); 22 | } 23 | 24 | // Method for getting Pages data 25 | getPages(cb){ 26 | this.api(this.pagesEndPoint).then((response)=>{ 27 | this.getPosts(response, cb) 28 | }); 29 | return true; 30 | } 31 | 32 | // Method for getting Posts data 33 | getPosts(pages, cb){ 34 | this.api(this.postsEndPoint).then((response)=>{ 35 | const posts = response 36 | const payload = { pages, posts }; 37 | 38 | this.getSuccess(payload); // Pass returned data to the store 39 | cb(payload); // This callback will be used for dynamic rout building 40 | }); 41 | return true; 42 | } 43 | 44 | // This returnes an object with Pages and Posts data together 45 | // The Alt Store will listen for this method to fire and will store the returned data 46 | getSuccess(payload){ 47 | return payload; 48 | } 49 | } 50 | 51 | export default alt.createActions(DataActions); -------------------------------------------------------------------------------- /webpack.production.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | module.exports = { 6 | devtool: 'source-map', 7 | devServer: { 8 | historyApiFallback: true, // This will make the server understand "/some-link" routs instead of "/#/some-link" 9 | }, 10 | entry: [ 11 | './src/scripts' // This is where Webpack will be looking for the entry index.js file 12 | ], 13 | output: { 14 | path: path.join(__dirname, 'build'), // This is used to specify folder for producion bundle 15 | filename: 'bundle.js', // Filename for production bundle 16 | publicPath: '/' 17 | }, 18 | resolve: { 19 | modules: [ 20 | 'node_modules', 21 | 'src', 22 | path.resolve(__dirname, 'src/scripts'), 23 | path.resolve(__dirname, 'node_modules') 24 | ], // Folders where Webpack is going to look for files to bundle together 25 | extensions: ['.jsx', '.js'] // Extensions that Webpack is going to expect 26 | }, 27 | module: { 28 | // Loaders allow you to preprocess files as you require() or “load” them. 29 | // Loaders are kind of like “tasks” in other build tools, and provide a powerful way to handle frontend build steps. 30 | loaders: [ 31 | { 32 | test: /\.jsx?$/, // Here we're going to use JS for react components but including JSX in case this extension is preferable 33 | include: [ 34 | path.resolve(__dirname, "src"), 35 | ], 36 | loader: ['react-hot-loader'] 37 | }, 38 | { 39 | loader: "babel-loader", 40 | 41 | // Skip any files outside of your project's `src` directory 42 | include: [ 43 | path.resolve(__dirname, "src"), 44 | ], 45 | 46 | // Only run `.js` and `.jsx` files through Babel 47 | test: /\.jsx?$/, 48 | 49 | // Options to configure babel with 50 | query: { 51 | plugins: ['transform-runtime'], 52 | presets: ['es2015', 'stage-0', 'react'], 53 | } 54 | } 55 | ] 56 | }, 57 | plugins: [ 58 | new webpack.NoEmitOnErrorsPlugin(), // Webpack will let you know if there are any errors 59 | 60 | // Declare global variables 61 | new webpack.ProvidePlugin({ 62 | React: 'react', 63 | ReactDOM: 'react-dom', 64 | _: 'lodash' 65 | }), 66 | 67 | new HtmlWebpackPlugin({ 68 | filename: 'index.html', 69 | template: './src/index.html', 70 | hash: true 71 | }), 72 | 73 | new webpack.optimize.UglifyJsPlugin({ 74 | compress: { 75 | warnings: false 76 | }, 77 | sourceMap: true 78 | }), 79 | ] 80 | } -------------------------------------------------------------------------------- /webpack.dev.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var path = require('path'); 3 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | module.exports = { 6 | devtool: 'cheap-module-source-map', 7 | devServer: { 8 | historyApiFallback: true, // This will make the server understand "/some-link" routs instead of "/#/some-link" 9 | }, 10 | entry: [ 11 | 'babel-polyfill', 12 | 'webpack-dev-server/client?http://127.0.0.1:8080/', // Specify the local server port 13 | 'webpack/hot/only-dev-server', // Enable hot reloading 14 | './src/scripts' // This is where Webpack will be looking for the entry index.js file 15 | ], 16 | output: { 17 | path: path.join(__dirname, 'build'), // This is used to specify folder for producion bundle 18 | filename: 'bundle.js', // Filename for production bundle 19 | publicPath: '/' 20 | }, 21 | resolve: { 22 | modules: [ 23 | 'node_modules', 24 | 'src', 25 | path.resolve(__dirname, 'src/scripts'), 26 | path.resolve(__dirname, 'node_modules') 27 | ], // Folders where Webpack is going to look for files to bundle together 28 | extensions: ['.jsx', '.js'] // Extensions that Webpack is going to expect 29 | }, 30 | module: { 31 | // Loaders allow you to preprocess files as you require() or “load” them. 32 | // Loaders are kind of like “tasks” in other build tools, and provide a powerful way to handle frontend build steps. 33 | loaders: [ 34 | { 35 | test: /\.jsx?$/, // Here we're going to use JS for react components but including JSX in case this extension is preferable 36 | include: [ 37 | path.resolve(__dirname, "src"), 38 | ], 39 | loader: ['react-hot-loader'] 40 | }, 41 | { 42 | loader: "babel-loader", 43 | 44 | // Skip any files outside of your project's `src` directory 45 | include: [ 46 | path.resolve(__dirname, "src"), 47 | ], 48 | 49 | // Only run `.js` and `.jsx` files through Babel 50 | test: /\.jsx?$/, 51 | 52 | // Options to configure babel with 53 | query: { 54 | plugins: ['transform-runtime'], 55 | presets: ['es2015', 'stage-0', 'react'], 56 | } 57 | } 58 | ] 59 | }, 60 | plugins: [ 61 | new webpack.HotModuleReplacementPlugin(), // Hot reloading 62 | new webpack.NoEmitOnErrorsPlugin(), // Webpack will let you know if there are any errors 63 | 64 | // Declare global variables 65 | new webpack.ProvidePlugin({ 66 | React: 'react', 67 | ReactDOM: 'react-dom', 68 | _: 'lodash' 69 | }), 70 | 71 | new HtmlWebpackPlugin({ 72 | filename: 'index.html', 73 | template: './src/index.html', 74 | hash: false 75 | }) 76 | ] 77 | } --------------------------------------------------------------------------------