The answer is meant to demonstrate an easily reproducible set of steps to get a React SPA indexed by Google without any need of SSR or prerendering. It's split into two parts with headings:
- Website Deployment
- Requesting Google to Index the Website
The first part is all about building a deploying a sample React application. Only one bullet point (that deals with title and canonical) is specific to SEO.
The second part is all about SEO, however it's not specific to SPA or React.
Website Deployment
The deployment is based on Crisp React boilerplate (I'm the author) and uses Cloudflare Pages.
This particular boilerplate has been chosen due to its features, like the variety of deployments, both Jamstack and full stack. It makes it easy to alter the deployment described below and switch from Jamstack to full stack if need be. The ready-to-replace Structured Data placeholders provided by the boilerplate could help with further SEO improvements once the website is indexed by Google.
The steps:
Clone Crisp React repository:
git clone https://github.com/winwiz1/crisp-react.git
cd crisp-react
Simplify the configuration by replacing the code fragment with the following code:
/****************** Start SPA Configuration ******************/
var SPAs = [
new SPA({
name: "index",
entryPoint: "./src/entrypoints/first.tsx",
ssr: false,
redirect: true
})];
SPAs.appTitle = "Crisp React";
/****************** End SPA Configuration ******************/
Additionally replace "Crisp React"
with your SPA title. It's important for SEO to have a unique and sensible title.
Review the client codebase to ensure each page sets the <title>
HTML element and the canonical <meta>
tag to the values that are meaningful for your website. This can be done by searching all the client/src/components/*.tsx
files for the <Helmet>
pattern and reviewing the relevant code:
<Helmet>
<title>{getTitle(pageName)}</title>
<link rel="canonical" href={getCanonical()} />
</Helmet>
// Simplified code
export const getCanonical = (pagePath?: string): string|undefined => {
return !!pagePath? (window.location.origin + pagePath) : window.location.href;
}
// Simplified code
export const getTitle = (pageTitle?: string): string => {
return !!pageTitle? `${SPAs.appTitle} - ${pageTitle}` : SPAs.appTitle;
}
Commit the changes:
git add client
git commit -m "Changed configuration"
Create a new GitHub repository by visiting repo.new.
Point the cloned repository to the newly created one and push it there:
git remote set-url origin https://github.com/your-github-username/your-newly-created-repo
git push
Deploy to Cloudflare Pages by logging into the Cloudflare dashboard and creating a Cloudflare Pages project.
This step will take several minutes spent mostly on waiting. It should take around a minute to copy the data provided below and paste it into the single configuration screen presented by Pages.
Use Menu > Pages > Create a project
. You will be asked to authorise read-only access to your GitHub repositories with an option to narrow the access to specific repositories. Select the repository which you pushed to GitHub at the previous step and on the "Set up builds and deployments" screen, provide the following information:
Configuration option |
Value |
Production branch |
master |
Build command |
yarn build:jamstack |
Build output directory |
client/dist |
Add the following environment variable:
Environment variable |
Value |
NODE_VERSION |
16.14.0 |
Optionally, you can customise the "Project name" field. It defaults to the GitHub repository name and is used to create a subdomain e.g. <project-name>.pages.dev
.
After completing the configuration, click on the "Save and Deploy" button. When the deployment pipeline finishes, point a browser to https://<project-name>.pages.dev
to check the website is online.
Finally use the 'Custom domains' tab to map the deployed website to a domain you own.
Requesting Google to Index the Website
Add some original content to the webpages to avoid duplicate content.
You can choose either passive approach and simply wait until Googlebot discovers your website or proactively ask Google to index it. If you choose the latter, use Google Search Console (GSC):
- Add your custom domain to GSC using the "+ Add Property" menu.
- Click on the "URL Inspection" menu to activate the URL Inspection Tool and type the path to the page you would like to index. The response will state that "URL is not on Google" telling you the page hasn't been indexed yet.
- Click on the
"TEST LIVE URL"
button to get confirmation the page can be indexed. Optionally review the screenshot of the page rendered by GSC.
- Request indexing for the page by clicking on the
"REQUEST INDEXING"
link. The response should say your request has been added to the priority crawl queue.
The last 3 steps will have to be repeated for each SPA page.
The deployed website lacks commonly used, though not strictly necessary for SEO files, such as sitemap.xml
and robots.txt
. Sitemap is more important for the passive approach. The robots.txt
file is not needed unless you want to put some crawling restrictions in place.
The website doesn't use all Crisp React features e.g. the ability to split a React app into multiple SPAs and selectively prerender the landing/index page of each SPA for better performance. If you need this functionality along with SEO, consider switching to full stack build or using a Cloudflare Worker as described in this article.