In this tutorial, you will build a full-stack Pages application. Your application will contain:
- A front end, built using Cloudflare Pages and the React framework.
- A JSON API, built with Pages Functions, that returns blog posts that can be retrieved and rendered in your front end.
If you prefer to work with a headless CMS rather than an API to render your blog content, refer to the headless CMS tutorial.
To begin, create a new Pages application using the React framework.
In your terminal, create a new React project called blog-frontend using the create-vite command. Go into the newly created blog-frontend directory and start a local development server:
npx create-vite -t react blog-frontend
cd blog-frontend
npm install
npm run devTo set up your React project:
- Install the React Router ↗ in the root of your
blog-frontenddirectory.
npm
i react-router-dom@6
yarn
add react-router-dom@6
pnpm
add react-router-dom@6
bun
add react-router-dom@6
- Clear the contents of
src/App.js. Copy and paste the following code to import the React Router intoApp.js, and set up a new router with two routes:
import { Routes, Route } from "react-router-dom";
import Posts from "./components/posts";
import Post from "./components/post";
function App() {
return (
<Routes>
<Route path="/" element={<Posts />} />
<Route path="/posts/:id" element={<Post />} />
</Routes>
);
}
export default App;- In the
srcdirectory, create a new folder calledcomponents. - In the
componentsdirectory, create two files:posts.js, andpost.js. These files will load the blog posts from your API, and render them. - Populate
posts.jswith the following code:
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
const Posts = () => {
const [posts, setPosts] = useState([]);
useEffect(() => {
const getPosts = async () => {
const resp = await fetch("/api/posts");
const postsResp = await resp.json();
setPosts(postsResp);
};
getPosts();
}, []);
return (
<div>
<h1>Posts</h1>
{posts.map((post) => (
<div key={post.id}>
<h2>
<Link to={`/posts/${post.id}`}>{post.title}</Link>
</h2>
</div>
))}
</div>
);
};
export default Posts;- Populate
post.jswith the following code:
import React, { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
const Post = () => {
const [post, setPost] = useState({});
const { id } = useParams();
useEffect(() => {
const getPost = async () => {
const resp = await fetch(`/api/post/${id}`);
const postResp = await resp.json();
setPost(postResp);
};
getPost();
}, [id]);
if (!Object.keys(post).length) return <div />;
return (
<div>
<h1>{post.title}</h1>
<p>{post.text}</p>
<p>
<em>Published {new Date(post.published_at).toLocaleString()}</em>
</p>
<p>
<Link to="/">Go back</Link>
</p>
</div>
);
};
export default Post;You will now create a Pages Functions that stores your blog content and retrieves it via a JSON API.
To create the Pages Function that will act as your JSON API:
- Create a
functionsdirectory in yourblog-frontenddirectory. - In
functions, create a directory namedapi. - In
api, create aposts.jsfile in theapidirectory. - Populate
posts.jswith the following code:
import posts from "./post/data";
export function onRequestGet() {
return Response.json(posts);
}This code gets blog data (from data.js, which you will make in step 8) and returns it as a JSON response from the path /api/posts.
- In the
apidirectory, create a directory namedpost. - In the
postdirectory, create adata.jsfile. - Populate
data.jswith the following code. This is where your blog content, blog title, and other information about your blog lives.
const posts = [
{
id: 1,
title: "My first blog post",
text: "Hello world! This is my first blog post on my new Cloudflare Workers + Pages blog.",
published_at: new Date("2020-10-23"),
},
{
id: 2,
title: "Updating my blog",
text: "It's my second blog post! I'm still writing and publishing using Cloudflare Workers + Pages :)",
published_at: new Date("2020-10-26"),
},
];
export default posts;- In the
postdirectory, create an[[id]].jsfile. - Populate
[[id]].jswith the following code:
import posts from "./data";
export function onRequestGet(context) {
const id = context.params.id;
if (!id) {
return new Response("Not found", { status: 404 });
}
const post = posts.find((post) => post.id === Number(id));
if (!post) {
return new Response("Not found", { status: 404 });
}
return Response.json(post);
}[[id]].js is a dynamic route which is used to accept a blog post id.
After you have configured your Pages application and Pages Function, deploy your project using the Wrangler or via the dashboard.
In your blog-frontend directory, run wrangler pages deploy to deploy your project to the Cloudflare dashboard.
wrangler pages deploy blog-frontendTo deploy via the Cloudflare dashboard, you will need to create a new Git repository for your Pages project and connect your Git repository to Cloudflare. This tutorial uses GitHub as its Git provider.
Create a new GitHub repository by visiting repo.new ↗. After creating a new repository, prepare and push your local application to GitHub by running the following commands in your terminal:
git init
git remote add origin https://github.com/<YOUR-GH-USERNAME>/<REPOSITORY-NAME>
git add .
git commit -m "Initial commit"
git branch -M main
git push -u origin mainDeploy your application to Pages:
-
In the Cloudflare dashboard, go to the Workers & Pages page.
Go to Workers & Pages ↗ -
Select Create application > Pages > Import an existing Git repository.
-
Select the new GitHub repository that you created and, in the Set up builds and deployments section, provide the following information:
| Configuration option | Value |
|---|---|
| Production branch | main |
| Build command | npm run build |
| Build directory | build |
After configuring your site, begin your first deploy. You should see Cloudflare Pages installing blog-frontend, your project dependencies, and building your site.
By completing this tutorial, you have created a full-stack Pages application.
- Learn about Pages Functions routing