How a lot time will we usually spend on undertaking setup? We’re speaking about configuring put in libraries and writing boilerplate code to construction and implement greatest practices for attaining optimum web site efficiency. At Brocoders, we regularly begin new initiatives from scratch. That is why, over three years in the past, we created a NestJS boilerplate for the backend in order that we would not should spend time creating core performance that the top person does not see however is essential for builders. Over this time, the boilerplate has acquired 1.9k stars on GitHub and has gained vital recognition past our firm. Now, we have determined to take it a step additional and created the Extensive React Boilerplate for the frontend. Its goal is to maintain our greatest practices in undertaking improvement collectively, avoiding acquainted pitfalls and decreasing improvement time.
Modules and Libraries Included in the Boilerplate
To have server-side rendering out of the box, automatic page caching, preloading, and other speed optimizations, we use the Next.js framework. It extends React by including static web site era and is a robust instrument for creating productive and Website positioning-friendly internet purposes. To make sure code reliability, efficiency, and readability within the boilerplate, TypeScript is utilized.
To organize the web site to help native languages and settings, we use an skilled language knowledgeable for internet purposes, the internationalization framework i18next. It helps manage all added language variations and adapt the content material of the location, menu, and messages for various languages and regional settings. We expanded it with packages to detect the person’s browser language and rework sources into the server-side of i18next.
Materials-UI is used for rapidly creating interfaces with out spending time writing elements from scratch, comparable to buttons, enter fields, tables, modal home windows, and extra. With darkish mode help, the appliance based mostly on the boilerplate is mechanically configured to make use of the person’s system theme.
React Hook Kind library is built-in for kind administration, offering a easy and intuitive API optimized for top efficiency as it really works with knowledge with out pointless re-renders of your entire kind.
“React Question” is used for state administration and knowledge caching. It mechanically optimizes queries, decreasing their duplication, and helps knowledge caching on each the consumer and server sides, permitting simple cache administration throughout environments.
The Cypress library gives an interface for monitoring and debugging exams, supporting varied varieties of exams, together with unit exams, integration exams, person interface exams, and extra.
ESLint helps to make sure that the fashion of the code within the undertaking is per the principles already established within the .eslintrc.json file to keep away from potential issues and warn about attainable errors.
The Architecture of the React Boilerplate Project and Folder Structure
The project structure allows for easy navigation and editing of various parts of the application. Automated tests are located in the /cypress folder, divided into different specifications for testing various aspects of the application. All source code of the project, following the logical structure of the application, is concentrated in the /src folder. Nested within it, the /app folder displays various application pages, such as the administrative panel with pages for creating and editing users, email confirmation pages, password reset, password change, user profile, login, and registration. The /components folder contains common components that can be used on different pages of the application. The services section is responsible for interacting with the API server; its files contain modules that are important for proper functionality and interaction with the backend and external services.
So far as this boilerplate makes use of the Subsequent.js framework for constructing React purposes, the folders are used as routes. This implies the extra folders you add to your app folder, the extra routes you’re going to get. Moreover, in the event you create a brand new folder inside one other folder, you’re going to get nested routes. To higher perceive these ideas, we advise wanting on the picture beneath.
We use dynamic segments in routing when versatile routes are wanted. Inside the file construction within the /app folder, such routes wrap the folder identify in sq. brackets. Thus, it’s simple to guess that the variable segments within the route src/app/[language]/admin-panel/customers/edit/[id]/ shall be language and id.
Mechanisms of Authentication and User Interaction
Since the web application supports internationalization, additional middleware is added to each page to determine the language, so the language of the authentication form will be displayed depending on the basic system settings of the user’s device.
Sign Up Page
The Sign Up page contains a registration form with fields for user registration, as well as the option to register via Google and Facebook. The necessary API for requests to the server to create a new account is specified, and saving user data is implemented using a context.
export function useAuthGoogleLoginService() {
const fetchBase = useFetchBase();
return useCallback(
(data: AuthGoogleLoginRequest) => {
return fetchBase(`${API_URL}/v1/auth/google/login`, {
method: "POST",
body: JSON.stringify(data),
}).then(wrapperFetchJsonResponse<AuthGoogleLoginResponse>);
},
[fetchBase]
);
}
export function useAuthFacebookLoginService() {
const fetchBase = useFetchBase();
return useCallback(
(data: AuthFacebookLoginRequest, requestConfig?: RequestConfigType) => {
return fetchBase(`${API_URL}/v1/auth/facebook/login`, {
method: "POST",
body: JSON.stringify(data),
...requestConfig,
}).then(wrapperFetchJsonResponse<AuthFacebookLoginResponse>);
},
[fetchBase]
);
}
Access and refresh tokens are acquired and stored for future requests if the backend status is ok. Otherwise, error-handling procedures are executed.
Sign In Page
The Sign In page contains an authentication form with fields for logging in an already registered user, and again, the option to log in via Google or Facebook. After successful authentication, the user receives an access token and a refresh token, which are stored for future requests.
if (status === HTTP_CODES_ENUM.OK) {
setTokensInfo({
token: data.token,
refreshToken: data.refreshToken,
tokenExpires: data.tokenExpires,
});
setUser(data.user);
  }
const setTokensInfo = useCallback(
(tokensInfo: TokensInfo) => {
setTokensInfoRef(tokensInfo);
if (tokensInfo) {
Cookies.set(AUTH_TOKEN_KEY, JSON.stringify(tokensInfo));
} else {
Cookies.remove(AUTH_TOKEN_KEY);
setUser(null);
}
},
[setTokensInfoRef]
 );
Restore and Update Password
A user may forget their password, so functionality for resetting the old password by sending a link to the email is created. Of course, for such cases, there should be a corresponding API on the server, like in our nestjs-boilerplate, which is perfect for two-way interaction.
Also, there is an ability to update the password. The logic of sending an API request to the server to update the user’s password and further processing its results is specified.
After registering a new account on the server, a link for email confirmation must be generated. Therefore, the boilerplate has logic for the confirm-email route as well.
Public and Private Routes
Both public and private routes are implemented – the user’s authorization is checked before displaying certain pages, and if the user is not authorized or the authorization data has not yet been loaded, the user is redirected to the sign-in page. Below is the HOC function that implements this logic:
function withPageRequiredAuth(
Component: FunctionComponent<PropsType>,
options?: OptionsType
) {
// …
return function WithPageRequiredAuth(props: PropsType) {
// …
useEffect(() => {
const check = () => {
if (
(user && user?.role?.id && optionRoles.includes(user?.role.id)) ||
!isLoaded
)
return;
const currentLocation = window.location.toString();
const returnToPath =
currentLocation.replace(new URL(currentLocation).origin, "") ||
`/${language}`;
const params = new URLSearchParams({
returnTo: returnToPath,
});
let redirectTo = `/${language}/sign-in?${params.toString()}`;
if (user) {
redirectTo = `/${language}`;
}
router.replace(redirectTo);
};
check();
}, [user, isLoaded, router, language]);
return user && user?.role?.id && optionRoles.includes(user?.role.id) ? (
<Component {...props} />
) : null;
};
}
Cypress tests have been added for sign-in, sign-up and forgot-password to detect errors and check that all the functionalities of the authentication types work on completely different browsers and units.
User’s Profile Management
The boilerplate includes user data pages and pages for editing their data. Functionality has been added to implement an avatar component that allows users to upload or change their profile photo.
The /profile/edit web page has been created to implement the flexibility to edit the profile, which features a kind with private knowledge that the person entered throughout registration, comparable to identify, surname, and password, in addition to including/altering an avatar. Moreover, to make sure code high quality, detect potential safety points, and confirm that the profile modifying performance works correctly, this a part of the code can be lined by Cypress exams.
describe("Validation and error messages", () => {
beforeEach(() => {
cy.go to("/sign-in");
});
it("Error messages ought to be displayed if required fields are empty", () => {
cy.getBySel("sign-in-submit").click on();
cy.getBySel("email-error").ought to("be.seen");
cy.getBySel("password-error").ought to("be.seen");
cy.getBySel("e mail").sort("useremail@gmail.com");
cy.getBySel("email-error").ought to("not.exist");
cy.getBySel("sign-in-submit").click on();
cy.getBySel("password-error").ought to("be.seen");
cy.getBySel("password").sort("password1");
cy.getBySel("password-error").ought to("not.exist");
cy.getBySel("e mail").clear();
cy.getBySel("email-error").ought to("be.seen");
});
it("Error message ought to be displayed if e mail is not registered within the system", () => {
cy.intercept("POST", "/api/v1/auth/e mail/login").as("login");
cy.getBySel("e mail").sort("notexistedemail@gmail.com");
cy.getBySel("password").sort("password1");
cy.getBySel("sign-in-submit").click on();
cy.wait("@login");
cy.getBySel("email-error").ought to("be.seen");
});
});
To automate the method of detecting and updating dependencies, we use the Renovate bot. It helps keep away from points associated to utilizing outdated dependencies and permits controlling the dependency replace course of in line with the undertaking’s wants.
Conclusion
We refer to the Extensive React Boilerplate as a structured start line for front-end improvement. It pairs superbly with our NestJS boilerplate for the backend, and with them, the event crew can get began, minimizing setup time and specializing in creating distinctive features of the undertaking, figuring out that fundamentals are already accurately applied. We additionally preserve monitor of normal library updates and keep the undertaking in an up-to-date state. So, welcome to strive it out 🙂