React JS
https://react.dev/learn/start-a-new-react-project
npx create-next-app@latest
npx create-react-app@latest {project name}
Create Project
C:\react-js>npx create-react-app@latest my-app
Run
C:\react-js\my-app> npm start
Install tailwindcss https://tailwindcss.com/docs/guides/create-react-app
npm install -D tailwindcss
npx tailwindcss
init Install
react-router-dom
https://www.npmjs.com/package/react-router-dom Install axios
src\App.js
//src\App.js import { BrowserRouter, Routes, Route } from "react-router-dom"; //npm i react-router-dom https://www.npmjs.com/package/react-router-dom import Home from "./elements/Home"; import ViewPage from "./elements/ViewPage"; function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Home />} /> <Route path="/posts/:slug" element={<ViewPage />} /> </Routes> </BrowserRouter> ); } export default App;src\elements\Home.js
//src\elements\Home.js import React from 'react'; import Posts from './Posts' import { Suspense } from "react"; import { IoSearch } from "react-icons/io5"; //https://www.npmjs.com/package/react-icons npm install react-icons --save function Home() { return ( <div className="w-screen py-20 flex justify-center flex-col items-center"> <div className="flex items-center justify-between gap-1 mb-5"> <h1 className="text-4xl font-bold">React.js WordPress Rest API Pagination with Featured Image and View Single Page | Tailwind CSS</h1> </div> <div className="overflow-x-auto py-10"> <div className="mb-2 w-full text-right"> <div className="relative flex flex-1"> <input type="text" className="w-full border border-gray-200 py-2 pl-10 text-sm outline-2 rounded-sm" placeholder="Search..." /> <IoSearch className="absolute left-3 top-2 h-5 w-5 text-gray-500" /> </div> </div> <Suspense fallback="Loading..."> <Posts/> </Suspense> </div> </div> ) } export default Homesrc\elements\Posts.js
//src\elements\Posts.js import React, { useEffect, useState } from 'react'; import axios from 'axios'; export default function Posts() { const [posts, setPosts] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(1); const perPage = 3; useEffect(() => { let url = `http://localhost:8888/cairocoders/wp-json/wp/v2/posts?per_page=${perPage}&page=${currentPage}&_embed`; axios.get(url).then((res) => { const { data, headers } = res; setTotalPages(Number(headers['x-wp-totalpages'])); setPosts(data); //console.log("Headers", headers['x-wp-totalpages']); }); }, [currentPage]) //console.log('posts', posts); return ( <div> <div className="grid md:grid-cols-3 gap-5 mt-10"> {posts.map((post, index) => ( <div key={index} className="max-w-sm border border-gray-200 rounded-md shadow"> <div className="relative aspect-video"> <img src={post._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url} alt={post.title.rendered} sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" className="rounded-t-md object-cover"/> </div> <div className="p-5"> <h1> <a href={`/posts/${post.slug}`}> {post.title.rendered} </a> </h1> </div> </div> ))} </div> { posts.length > 0 && ( <div className='w-1/2 py-10 m-auto flex justify-between items-center align-middle flex-wrap gap-10'> <button className='btn-primary p-2 bg-blue-500 text-white text-lg rounded-lg hover:shadow-lg disabled:opacity-50' disabled={currentPage === 1} onClick={() => setCurrentPage(currentPage - 1)} > Previous </button> <span className='text-lg'>{currentPage} of {totalPages}</span> <button className='btn-primary p-2 bg-blue-500 text-white text-lg rounded-lg hover:shadow-lg disabled:opacity-50' disabled={currentPage === totalPages} onClick={() => setCurrentPage(currentPage + 1)} > Next </button> </div> ) } </div> ); }src\elements\ViewPage.js
//src\elements\ViewPage.js import axios from 'axios'; import React, { useEffect, useState } from 'react' import { useParams } from 'react-router-dom'; const Single = () => { const { slug } = useParams(); const [post, setPost] = useState({}); useEffect(()=>{ // axios let url = "http://localhost:8888/cairocoders/wp-json/wp/v2/posts?_embed&slug="+slug; axios.get(url).then(res => { console.log('res', res); //console.log(res.data[0].id); setPost(res.data); }).catch(err => { console.log('error:', err.message); }); }, []); return ( <div className="min-h-screen flex items-center justify-center bg-slate-100"> <div className="bg-white rounded-sm shadow p-8"> { post.length ? ( <div> <h1 className="text-2xl font-bold mb-5">{post[0].title.rendered}</h1> <div className="mb-4"> <div dangerouslySetInnerHTML={{ __html: post[0]['content']['rendered'] }} /> </div> </div> ) : ('Loading....') } </div> </div> ) } export default Single;themes\cairocoders\functions.php
//themes\cairocoders\functions.php <?php /** * Theme functions and definitions * * @package cairocoders */ add_action('rest_api_init', 'register_rest_images' ); function register_rest_images(){ register_rest_field( array('post'), 'fimg_url', array( 'get_callback' => 'get_rest_featured_image', 'update_callback' => null, 'schema' => null, ) ); } function get_rest_featured_image( $object, $field_name, $request ) { if( $object['featured_media'] ){ $img = wp_get_attachment_image_src( $object['featured_media'], 'app-thumb' ); return $img[0]; } return false; }Run C:\react-j\my-app>npm start
http://localhost:3000/