How to Make Your React Web App SEO-Friendly

React is an open source library which has been developed and maintained by Facebook for building interactive UI components for both mobile apps and Single Page Applications (SPA).

It runs on JavaScript and is popularly used by developers as it boosts productivity during the development process.

SPA problems with SEO

The way these sites are built makes it harder for search engines to crawl their content.

What happens when you use React without server-side rendering is that the crawler halts on the very first page because it can’t see any hyperlinks to follow. It sends the page to the indexer, which then has to render the page and extracts the hyperlinks, which will then be added to the crawler’s queue. Then the crawler will eventually crawl the next set of pages, and again will stop there because all the links are invisible until the JavaScript is rendered. So it has to wait for the indexer to come back with a new set of URLs to crawl.”

But this can be improved in 2 ways

  1. React Helmet
  2. Isomorphic React Applications

What is a React Helmet?

React Helmet is a library that helps you deal with search engines and social media crawlers by adding meta tags to your pages/components on React so your site gives more valuable information to the crawlers.

React Helmet Usage

Let’s assume we have a React application with a Home Component, which is the landing page of a business which sells products for pets.

[code language=”css”]
Import React from ‘react’;’
Import ProductList from ‘../components/ProductList’;
Const Home = () =>{
Return <ProductList/>
};
Export default Home;
[/code]

This is a basic component that doesn’t include any Meta data useful for search engines and social media crawlers, so we need React Helmet to do it.

To start using React Helmet we need to install the library as follows:

npm install -save react-helmet

Then we need to modify the Home component to start using Helmet in a way that we can use title tags and Meta tags:

[code language=”css”]
import React from ‘react’;
import { Helmet } from ‘react-helmet’;
import ProductList from ‘../components/ProductList’;
const Home = () => {
return (
<>
<Helmet>
<title>Pets – Products</title>
<meta name="description" content="Find all the best quality products your pet may need" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@user" />
<meta name="twitter:creator" content="@user" />
<meta name="twitter:title" content="Pets – Products" />
<meta name="twitter:description" content="Best Products for your pet" />
<meta name="twitter:image" content="url_to_image"/>
<meta property="og:description" content="Best Products for your pet" />
<meta property="og:url" content="pets.abc" />
<meta property="og:site_name" content="Pets – Products" />
<meta property="og:locale" content="en_US" />
<meta property="og:type" content="article" />
<meta property="fb:app_id" content="ID_APP_FACEBOOK" />
</Helmet>
<ProductList /></>)};
export default Home;
[/code]

Isomorphic React Applications

Isomorphic React App is a web app that consists of code that can run on both server and client-side.

An isomorphic app defines whether the client is able to run scripts or not. When JavaScript is turned off, the code is rendered on the server, so a browser or bot gets all Meta tags and content in HTML and CSS.

When JavaScript is on, only the first page is rendered on the server, so the browser gets HTML, CSS, and JavaScript files. Then JavaScript starts running and the rest of the content is loaded dynamically.

The first screen is displayed faster, the app is compatible with older browsers, and user interactions are smoother in contrast to when websites are rendered on the client side.

Building an isomorphic app can be really time-consuming. There are frameworks that facilitate this process. The two most popular solutions for SEO are Next.js and Gatsby.

  1. Next.js is a framework that helps you create react apps that are generated on the server side quickly and without hassle. It also allows for automatic code splitting and hot code reloading.

    Next.js can do full-fledged server-side rendering, meaning HTML is generated for each request right when the request is made.

  2. Gatsby is a free open-source compiler that allows developers to make fast and powerful websites. Gatsby doesn’t offer full-fledged server-side rendering.

    Instead, it generates a static website beforehand and stores generated HTML files in the cloud or on the hosting service. Let’s take a closer look at their approaches.

Server-side rendering with Next.js

  1. The Next.js server, running on Node.js, receives a request and matches it with a certain page (a React component) using a URL address.
  2. The page can request data from an API or database, and the server will wait for this data.
  3. The Next.js app generates HTML and CSS based on the received data and existing React components.
  4. The server sends a response with HTML, CSS, and JavaScript.

Server side rendering with Nextjs

Making website SEO Friendly with GatsbyJS

The process of optimizing React applications is divided into two phases: generating a static website during the build and processing requests during runtime.

The build time process looks as follows:

  1. Gatsby’s bundling tool receives data from an API, CMS, and file system.
  2. During deployment or setting up a CI/CD pipeline, the tool generates static HTML and CSS on the basis of data and React components.
  3. After compilation, the tool creates an about folder with an index.html file. The website consists of only static files, which can be hosted on any hosting service or in the cloud.

Request processing during runtime happens like this:

  1. Gatsby instantly sends HTML, CSS, and JavaScript files to the requested page, since they already were rendered during compilation.
  2. After JavaScript is loaded to the browser, the website starts working like a typical React app.

    You can dynamically request data that isn’t important for SEO and work with the website just like you work with a regular single-page react app.

 

Built TimeRun Time

Creating an isomorphic app is considered the most reliable way to make React SEO-compatible, but it’s not the only option.

Conclusion:

SEO-friendliness of single-page applications develop with react is still a challenge. To make an SPA visible for Google crawlers and available for indexing, you might opt for per-rendering or server-side rendering.

Frameworks like Next.js, makes the work of an SEO much easier. You can also overcome the all the SEO-challenges with the help of the above solutions and frameworks.

I hope the above react SEO guide helps you. To develop your new application with react or any further assistance you can contact Andolasoft experienced react developers.

 

How To Manage API Request with AXIOS on a React Native App

APIs can make your life a whole lot easier. With an API, you can send requests to other services and get responses without having to build those requests and responses yourself. But building an API isn’t as simple as it sounds. It requires careful planning, testing, and debugging.

If you’re building an API for the first time, it can feel like an impossible mountain to climb. That’s where APIs like Axios come in. It has a great API and lots of helpful features. Here in this article you’ll understand how to use Axios to manage API requests in your React Native app.

What is AXIOS?

Axios is one of the easiest HTTP clients to learn and use. Making an API request is as simple as passing a configuration object to Axios or invoking the appropriate method with the necessary arguments. You will learn the basics of Axios in this section.

Configuring Axios

Type following command on terminal window to install Axios:

NPM Install Axios

How to make requests to an API using Axios

Making a call to an API using Axios, you can pass a configuration object to Axios or invoke a method for the corresponding CRUD operations.

Never miss an update from us. Join 10,000+ marketers and leaders.

For example, you can make a GET request to the /api/users endpoint in one of the following two ways:

[code language=”css”]</pre>
import axios from ‘axios’;
const baseUrl = ‘https://reqres.in’;
// Passing configuration object to axios
axios({
method: ‘get’,
url: `${baseUrl}/api/users/1`,
}).then((response) => {
console.log("<<<<<< Passing configuration object to axios >>>>>>", response.data.data);
});

// Invoking get method to perform a GET request
axios.get(`${baseUrl}/api/users/1`).then((response) => {
console.log("<<<<<< Invoking get method to perform a GET request >>>>>>", response.data.data);
});
[/code]

There are several other fields such as baseURL, transformRequest, transformResponse, and headers, among others, which you can include in the configuration object you pass to Axios.

[code language=”css”]</pre>
// Passing configuration object to axios
const fetchUserFirst = async () => {
const configurationObject = {
method: ‘get’,
url: `${baseUrl}/api/users/1`,
};
const response = await axios(configurationObject);
console.log("<<<<<< Fetch User First >>>>>>", response.data.data);
};

// Invoking get method to perform a GET request
const fetchUserSecond = async () => {
const url = `${baseUrl}/api/users/2`;
const response = await axios.get(url);
console.log("<<<<<< Fetch User Second >>>>>>", response.data.data);
};
[/code]

How to make multiple concurrent API requests using Axios

We can use the Promise.all or Promise.allSettled method of the Promise API with Axios to make multiple concurrent API requests from a React Native application.

[code language=”css”]
const concurrentRequests = [
axios.get(`${baseUrl}/api/users/1`),
axios.get(`${baseUrl}/api/users/2`),
axios.get(`${baseUrl}/api/users/3`),
];
// Using Promise.all
Promise.all(concurrentRequests)
.then((result) => {
console.log(result);
})
.catch((err) => {
console.log(err);
});
// Using Promise.allSettled
Promise.allSettled(concurrentRequests)
.then((result) => {
console.log(result);
})
.catch((err) => {
console.log(err);
});
[/code]

How to abort network request in Axios

Axios provides functionality for aborting network requests. A typical use case of this feature in React Native is the cancellation of network requests in the use effect hook when a component is unmounted while data is still in flight.

[code language=”css”]
useEffect(() => {
const source = axios.CancelToken.source();
const url = `${baseUrl}/api/users/${userId}`;
const fetchUsers = async () => {
try {
const response = await axios.get(url, { cancelToken: source.token });
console.log(response.data);
} catch (error) {
if(axios.isCancel(error)){
console.log(‘Data fetching cancelled’);
}else{
// Handle error
}
}
};
fetchUsers();
return () => source.cancel("Data fetching cancelled");
}, [userId]);
[/code]

How to create an instance of Axios

You can also create an instance of Axios with a custom configuration. Axios will merge the configuration object passed while creating the instance with the configuration passed to the instance method:

[code language=”css”]
const axiosInstance = axios.create({ baseURL: ‘https://reqres.in/’ });
axiosInstance.get(‘api/users/1’).then((response) => {
console.log(response.data);
});
[/code]

How to make GET request using Axios in React Native

Make a GET request to the /api/users endpoint to retrieve a user and store the user ID in state as shown in the code snippet below. You can change the user ID inside the onPress event handler attached to the Load User button. Changing the user ID will trigger a GET request to the API inside the useEffect hook.

After triggering a network request, we display a loading indicator on the screen. If we fetch the data successfully, we update state and remove the loading indicator. If we fail to retrieve the data for some reason, we stop the loading indicator and display an appropriate error message.

We abort the network request in the clean-up function if the user decides to close the app before getting a response from the server. Check the return value of the effect function in the useEffect hook. Following is the code in the App.js component:

[code language=”css”]
import axios from "axios";
import React, { useState, useEffect } from "react";
import {
StyleSheet,
Text,
ScrollView,
View,
Button,
Image,
Platform,
} from "react-native";
import Constants from "expo-constants";
const baseUrl = "https://reqres.in";
function User({ userObject }) {
return (
<View>
<Image
source={{ uri: userObject.avatar }}
style={{ width: 128, height: 128, borderRadius: 64 }}
/>
<Text style={{ textAlign: "center", color: "white" }}>
{`${userObject.first_name} ${userObject.last_name}`}
</Text>
</View>
);
}
export default function App() {
const [userId, setUserId] = useState(1);
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [hasError, setErrorFlag] = useState(false);
const changeUserIdHandler = () => {
setUserId((userId) => (userId === 3 ? 1 : userId + 1));
};
useEffect(() => {
const source = axios.CancelToken.source();
const url = `${baseUrl}/api/users/${userId}`;
const fetchUsers = async () => {
try {
setIsLoading(true);
const response = await axios.get(url, { cancelToken: source.token });
if (response.status === 200) {
setUser(response.data.data);
setIsLoading(false);
return;
} else {
throw new Error("Failed to fetch users");
}
} catch (error) {
if(axios.isCancel(error)){
console.log(‘Data fetching cancelled’);
}else{
setErrorFlag(true);
setIsLoading(false);
}
}
};
fetchUsers();
return () => source.cancel("Data fetching cancelled");
}, [userId]);
return (
<ScrollView contentContainerStyle={styles.container}>
<View style={styles.wrapperStyle}>
{!isLoading && !hasError && user && <User userObject={user} />}
</View>
<View style={styles.wrapperStyle}>
{isLoading && <Text> Loading </Text>}
{!isLoading && hasError && <Text> An error has occurred </Text>}
</View>
<View>
<Button
title="Load user"
onPress={changeUserIdHandler}
disabled={isLoading}
style={styles.buttonStyles}
/>
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "dodgerblue",
alignItems: "center",
justifyContent: "center",
marginTop: Platform.OS === "ios" ? 0 : Constants.statusBarHeight,
},
wrapperStyle: {
minHeight: 128,
},
buttonStyles: {
padding: 100,
},
});
[/code]

How to make a POST request

POST is the HTTP method you use to send data to the server for updating or creating a resource. Making a POST request in Axios is similar to making a GET request. Most of the time, POST requests are made with user-generated data submitted using a form. Data requires validation on the client side before it is submitted.

Two main React packages for managing forms are Formik and React Hook Form. React Native form for the user’s full name and email in the code snippet below. Both TextInput components are controlled components.

After clicking the submit button, the TextInput fields and the submit button are disabled before you display a message to show you are creating the resource. Disabling the submit button ensures the user doesn’t make multiple submissions. After successfully submitting a POST request, you display a success message to the user:

[code language=”css”]
import axios from "axios";
import React, { useState, useEffect } from "react";
import {
StyleSheet,
Text,
ScrollView,
View,
Button,
Image,
Platform,
} from "react-native";
import Constants from "expo-constants";
const baseUrl = "https://reqres.in";
function User({ userObject }) {
return (
<View>
<Image
source={{ uri: userObject.avatar }}
style={{ width: 128, height: 128, borderRadius: 64 }}
/>
<Text style={{ textAlign: "center", color: "white" }}>
{`${userObject.first_name} ${userObject.last_name}`}
</Text>
</View>
);
}
export default function App() {
const [userId, setUserId] = useState(1);
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const [hasError, setErrorFlag] = useState(false);
const changeUserIdHandler = () => {
setUserId((userId) => (userId === 3 ? 1 : userId + 1));
};
useEffect(() => {
const source = axios.CancelToken.source();
const url = `${baseUrl}/api/users/${userId}`;
const fetchUsers = async () => {
try {
setIsLoading(true);
const response = await axios.get(url, { cancelToken: source.token });
if (response.status === 200) {
setUser(response.data.data);
setIsLoading(false);
return;
} else {
throw new Error("Failed to fetch users");
}
} catch (error) {
if(axios.isCancel(error)){
console.log(‘Data fetching cancelled’);
}else{
setErrorFlag(true);
setIsLoading(false);
}
}
};
fetchUsers();
return () => source.cancel("Data fetching cancelled");
}, [userId]);
return (
<ScrollView contentContainerStyle={styles.container}>
<View style={styles.wrapperStyle}>
{!isLoading && !hasError && user && <User userObject={user} />}
</View>
<View style={styles.wrapperStyle}>
{isLoading && <Text> Loading </Text>}
{!isLoading && hasError && <Text> An error has occurred </Text>}
</View>
<View>
<Button
title="Load user"
onPress={changeUserIdHandler}
disabled={isLoading}
style={styles.buttonStyles}
/>
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "dodgerblue",
alignItems: "center",
justifyContent: "center",
marginTop: Platform.OS === "ios" ? 0 : Constants.statusBarHeight,
},
wrapperStyle: {
minHeight: 128,
},
buttonStyles: {
padding: 100,
},
});
[/code]

How to make a DELETE request

DELETE requests using Axios the same way you make POST and PUT requests. DELETE request will delete a resource from the server side. You can replace the onSubmitFormHandler of the code for making a POST request with the event handler below to make a DELETE request.

[code language=”css”]
const onSubmitFormHandler = async (event) => {
if (!fullName.trim() || !email.trim()) {
alert("Name or Email is invalid");
return;
}
setIsLoading(true);
try {
const response = await axios.delete(`${baseUrl}/api/users/2`, {
fullName,
email,
});
if (response.status === 204) {
alert(` You have deleted: ${JSON.stringify(response.data)}`);
setIsLoading(false);
setFullName(”);
setEmail(”);
} else {
throw new Error("Failed to delete resource");
}
} catch (error) {
alert("Failed to delete resource");
setIsLoading(false);
}
};
[/code]

How to make a PUT request

Updating a resource requires either the PUT or PATCH method. If a resource exists, using the PUT method completely overwrites it, and creates a new resource if it doesn’t. PATCH makes partial updates to the resource if it exists and does nothing if it doesn’t.

Making a PUT request to an API is similar to making a POST request. The only difference is the configuration object passed to Axios, or the HTTP method needed to invoke to make a PUT request to the API. Replace the onSubmitFormHandler of the POST request with the code below to make a PUT request.

[code language=”css”]</pre>
const onSubmitFormHandler = (event) => {
if (!fullName.trim() || !email.trim()) {
alert("Name or Email is invalid");
return;
}
setIsLoading(true);
const configurationObject = {
url: `${baseUrl}/api/users/2`,
method: "PUT",
data: { fullName, email },
};
axios(configurationObject)
.then((response) => {
if (response.status === 200) {
alert(` You have updated: ${JSON.stringify(response.data)}`);
setIsLoading(false);
setFullName("");
setEmail("");
} else {
throw new Error("An error has occurred");
}
})
.catch((error) => {
alert("An error has occurred");
setIsLoading(false);
});
};
[/code]

How to handle errors

React-error-boundary (Simple reusable React error boundary component) is a simple reusable component based on React error boundary API that provides a wrapper around your components and automatically catches all errors from the children’s components hierarchy, and also provides a great way to recover your component tree. Create an Errorhandler component like the following code snippet.

[code language=”css”]</pre>
import * as React from "react";
import { ErrorBoundary } from "react-error-boundary";
import { View, StyleSheet, Button } from "react-native";
import { Text } from "components";
const myErrorHandler = (error: Error) => {
// Do something with the error
function ErrorFallback({ resetErrorBoundary }) {
return (
<View style={[styles.container]}>
<View>
<Text> Something went wrong: </Text>
<Button title="try Again" onPress={resetErrorBoundary} />
</View>
</View>
);
}
export const ErrorHandler = ({ children }: { children: React.ReactNode }) => (
<ErrorBoundary FallbackComponent={ErrorFallback} onError={myErrorHandler}>
{children}
</ErrorBoundary>
);
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "column",
alignItems: "stretch",
justifyContent: "center",
alignContent: "center",
paddingHorizontal: 12,
},
});
[/code]

Here you can find the sample code in this Github repository

Best Practices for using AXIOS

Global config

Set up a global configuration that handles all application requests using a standard configuration that is set through a default object that ships with Axios. This object contains:

    • baseURL: A relative URL that acts as a prefix to all requests, and each request can append the URL
    • headers: Custom headers that can be set based on the requests
    • Timeout: The point at which the request is aborted, usually measured in milliseconds. The default value is 0, meaning it’s not applicable.
    • With Credentials: Indicates whether or not cross-site Access-Control requests should be made using credentials. The default is false.
    • Response Type: Indicates the type of data that the server will return, with options including json (default), arraybuffer, document, text, and stream.
    • Response Encoding: Indicates encoding to use for decoding responses. The default value is utf8.
    • xsrfCookieName: The name of the cookie to use as a value for XSRF token, the default value is XSRF-TOKEN.
    • xsrfHeaderName: The name of the HTTP header that carries the XSRF token value. The default value is X-XSRF-TOKEN.
    • maxContentLength: Defines the max size of the HTTP response content in bytes allowed
    • maxBodyLength: Defines the max size of the HTTP request content in bytes allowed

Most of the time, only be using baseURL, header, and maybe timeout. The rest of them are less frequently needed as they have smart defaults, but it’s nice to know they are there in case you need to fix up requests.

Are you looking for a React Native developer

Contact Us

This is the DRYness at work. For each request, we don’t have to repeat the baseURL of our API or repeat important headers that we might need on every request.

Custom instance

Setting up a “custom instance” is similar to a global config, but scoped to specified components so that it’s still a DRY technique, but with hierarchy. Set up a custom instance in a new file (Ex: authAxios.js) and import it into the “concern” components.

[code language=”css”]
// authAxios.js
import axios from ‘axios’;
const customInstance = axios.create ({
baseURL : ‘https://axios-app.firebaseio.com’
})
customInstance.defaults.headers.post[‘Accept’] = ‘application/json’
// Or like this…
const customInstance = axios.create ({
baseURL : ‘https://axios-app.firebaseio.com’,
headers: {‘Accept’: ‘application/json’}
})
[/code]

Then import this file into the “concern” components:

[code language=”css”]

// form.js component import from our custom instance
import axios from ‘./authAxios’;
export default {
methods : {
onSubmit () {
axios.post(‘/users.json’, formData)
.then(res => console.log(res))
.catch(error => console.log(error))
}
}
}
[/code]

Axios Verbs

Group the Axios HTTP verbs, like GET, POST, DELETE, and PATCH, in the base config file, as below.

[code language=”css”]</pre>
export function getRequest(URL) {

return axiosClient.get(`/${URL}`).then(response => response);

}

export function postRequest(URL, payload) {

return axiosClient.post(`/${URL}`, payload).then(response => response);

}

export function patchRequest(URL, payload) {

return axiosClient.patch(`/${URL}`, payload).then(response => response);

}

export function deleteRequest(URL) {

return axiosClient.delete(`/${URL}`).then(response => response);
<pre>}
[/code]

Now import the custom functions directly wherever needed to make an API request, as in the code below.

[code language=”css”]</pre>
import { getRequest } from ‘axiosClient’;

async function fetchUser() {

try {

const user = await getRequest(‘users’);

} catch(error) {

//Log errors

}
<pre>}
[/code]

Interceptors

  • Interceptors helps with cases where the global config or custom instance might be too generic, in the sense that if you set up a header within their objects, it applies to the header of every request within the affected components. Interceptors have the ability to change any object properties on the fly. For instance, we can send a different header based on any condition we choose within the interceptor.
  • Interceptors can be in the main.js file or a custom instance file. Requests are intercepted after they’ve been sent out and allow us to change how the response is handled.

[code language=”css”]
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent, like we’re inserting a timeout for only requests with a particular baseURL
if (config.baseURL === ‘https://axios-app.firebaseio.com/users.json’) {
config.timeout = 4000
} else {
return config
}
console.log (config)
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data like console.log, change header, or as we did here just added a conditional behaviour, to change the route or pop up an alert box, based on the reponse status
if (response.status === 200 || response.status 201) {
router.replace(‘homepage’) }
else {
alert(‘Unusual behaviour’)
}
console.log(response)
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
[/code]

Conclusion

For most of your HTTP communication needs, Axios provides an easy-to-use API in a compact package.

There are some alternative libraries for HTTP communication, such as ky, a tiny and elegant HTTP client based on window.fetch; superagent, a small, progressive client-side HTTP request library based on XMLHttpRequest.

But Axios is a better solution for applications with a lot of HTTP requests and for those that need good error handling or HTTP interceptions.

We at Andolasoft has long expertise on API Request solution with AXIOS on a React Native App. We have highly experienced React Native and React developers to help you for the same. Book a free consultation on your issues.