I wanted to create some documentation that was private. That was all I wanted to do. On my journey though I found that there wasn't many resources out there that allowed me to do this. That is when I found an article that didn't work for Docusaurus 3.1 and used Firebase a library that I didn't want to use. So I had to do what all programmers do and that is jag it. See below for how I did it and how you can do it to.
Prerequisites
- Working installation of Docusaurus 3.1 - https://docusaurus.io/docs/installation
- A library to authenticate with. Try Pocketbase or another SDK.
- 30 minutes of your time
Files
Create 3 new files under the theme folder of your Docusaurus. If the theme folder doesn't exist create it.
files to create: theme/ColorModeToggle/index.js
, theme/pocketbase.js
, theme/Root.js
Note: You can tell that it is the Docusaurus installation by locating the folder that the .docusaurus
installation is located at.
Authentication
Here is the below code I used to setup Authentication in pocketbase. Place this code in your pocketbase.js
file
import PocketBase from "pocketbase";
//initialize pocketbase
const app = (() => {
const POCKETBASE_URL = "https://fastpocket.fly.dev" + "/";
return new PocketBase(POCKETBASE_URL);
})();
//pass authstore to be used to callback when the user is authenticated
export const auth = app.authStore;
//clears the auth and logs the user out
export const logout = (afterAction = () => {}) => {
app.authStore.clear();
afterAction(null);
};
//method to authenticate the user with pocketbase
export const signIn = async (email, password) => {
try {
await app.collection("user").authWithPassword(email, password);
} catch (err) {
console.error(err);
alert(err.message);
}
};
While the auth that is happening is being done using Pocketbase the principals apply to any SDK/Auth framework.
- Authenticate
- Pass a callback to the Root component to tell whenever the authentication state changes
Root Override
Now we want to override the root component to display our authentication challenge if the user hasn't signed in. We do this by making a conditional render. Here {children}
is the documentation and the other code is our custom auth page.
import React, { useState } from "react";
import { signIn, auth } from "./pocketbase";
export default function Root({ children }) {
const [userAuth, setUserAuth] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true)
await signIn(email,password)
setLoading(false)
};
auth.onChange(async function (user) {
if (user !== null) {
setUserAuth(user);
}
});
return (
<>
{userAuth != "" ? (
<>{children}</>
) : (
<div
style={{
flexGrow: 1,
display: "flex",
justifyItems: "center",
alignItems: "center",
marginRight: "auto",
marginLeft: "auto",
flexDirection: 'column'
}}
>
<form
onSubmit={handleSubmit}
style={{
display: "flex",
flexDirection: "column",
width: "200px",
margin: "auto",
}}
>
<img src="img/combination-icon.png" />
<h1 style={{marginLeft: 'auto', marginRight: 'auto'}}>Docs</h1>
<label htmlFor="email" style={{ marginBottom: "10px" }}>
Email:
</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
style={{
marginBottom: "10px",
padding: "5px",
borderRadius: "5px",
border: "1px solid #ccc",
}}
/>
<label htmlFor="password" style={{ marginBottom: "10px" }}>
Password:
</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
style={{
marginBottom: "10px",
padding: "5px",
borderRadius: "5px",
border: "1px solid #ccc",
}}
/>
<button
type="submit"
style={{
padding: "5px",
borderRadius: "5px",
border: "none",
backgroundColor: "#fd5367",
color: "white",
marginTop: '1rem'
}}
disabled={loading}
>
{loading ? 'Loading...':'Login'}
</button>
</form>
</div>
)}
</>
);
}
Logout Button
Finally we want to create a logout button by extending the ColorModeToggle
component that controls the light or dark theme. We are going to add our own <a />
tag that will appear beside the ColorModeToggle
component and act as our logout button.
import React from 'react';
import ColorModeToggle from '@theme-original/ColorModeToggle';
import {logout} from '../pocketbase';
export default function ColorModeToggleWrapper(props) {
return (
<>
<ColorModeToggle {...props} />
<a style={{background: '#fd5469', color: 'white', paddingRight: 10, paddingLeft: 10, paddingBottom: 2, borderRadius: 5, marginLeft: 15, cursor: "pointer"}} onClick={() => logout(() => window.location.reload())}>
Logout
</a>
</>
);
}
Putting It All Together
Finally you will want to run npm start
to get your code to run and you should be greated with a logon screen.
Explaination
Docusaurus provides you with the ability to override the React components that compose the documentation. They do this through swizzling which is the lingo for basically overriding the components through the Docusaurus/themes folder. What we are doing is overriding the root component with our authentication and ensuring that the user has to pass the authentication challenge before entering the site.
Build Fast With FastPocket
If this article helped you develop fast you should see what you can do with FastPocket. FastPocket is the number 1 Pocketbase/React boilerplate to get you building amazing apps very quickly. It features everything that you need to start making money with your own apps from Authentication to Payments. Get it today!