What you will find in this article?
In the era of information sharing, protecting sensitive content is crucial. Notion has become a popular tool for content management, but what if you need to restrict access?
Email protection is a straightforward yet effective way to control who accesses your content. By requiring visitors to enter their email, you not only restrict access but also gather potential leads or feedback.
In this tutorial, we'll walk through building an email-protected Notion page using Next.js and react-notion-x
.
Papermark - the open-source DocSend alternative.
Before we kick it off, let me share Papermark with you. It's an open-source alternative to DocSend that helps you securely share documents from PDFs to Notion pages and get real-time page-by-page analytics from viewers. It's all open-source!
I would be grateful if you could give us a star! Don't forget to share your thoughts in the comments section โค๏ธ
https://github.com/mfts/papermark
Setup the project
Let's go ahead and set up our project environment for our Notion Lead Generator application. We'll be creating a Next.js app, react-notion-x and Tailwind CSS.
Setting up Next.js with TypeScript and Tailwindcss
We'll use create-next-app
to generate a new Next.js project. We'll also be using TypeScript and Tailwind CSS, so make sure to select those options when prompted.
npx create-next-app
# ---
# you'll be asked the following prompts
What is your project named? my-app
Would you like to add TypeScript with this project? Y/N
# select `Y` for typescript
Would you like to use ESLint with this project? Y/N
# select `Y` for ESLint
Would you like to use Tailwind CSS with this project? Y/N
# select `Y` for Tailwind CSS
Would you like to use the `src/ directory` with this project? Y/N
# select `N` for `src/` directory
What import alias would you like configured? `@/*`
# enter `@/*` for import alias
Setting up react-notion-x
Next, we'll install react-notion-x
to fetch our Notion page data. notion-client
, notion-utils
, and notion-types
are peer dependencies and contain helper methods for react-notion-x
, so we'll install those as well.
npm install react-notion-x notion-client notion-utils notion-types
Building the application
Now that we have our setup in place, we are ready to start building our application. The main features we'll cover are:
- Render Notion page content in React
- Add email capture form to the page
#1 Render Notion page content in React
Let's create a component that contains the Notion page content. We'll create a new component components/notion-page.tsx
and add the following code to it.
// components/notion-page.tsx
import { ExtendedRecordMap } from "notion-types";
import { NotionRenderer } from "react-notion-x";
// core styles shared by all of react-notion-x (required)
import "react-notion-x/src/styles.css";
export const NotionPage = ({ recordMap }: { recordMap: ExtendedRecordMap }) => {
if (!recordMap) {
return null;
}
return (
<div className="bg-white">
<NotionRenderer
recordMap={recordMap}
fullPage={true}
darkMode={false}
disableHeader={true}
/>
</div>
);
};
// pages/index.tsx
import { NotionAPI } from "notion-client";
import { type ExtendedRecordMap } from "notion-types";
import { parsePageId } from "notion-utils";
import { type GetStaticPropsContext } from "next";
const notion = new NotionAPI();
export const getStaticProps = async (context: GetStaticPropsContext) => {
const notionUrl = "https://www.notion.so/..."; // enter your Notion page URL here
let pageId = null;
let recordMap = null;
const notionPageId = parsePageId(file, { uuid: false });
if (!notionPageId) {
return {
notFound: true,
};
}
pageId = notionPageId;
recordMap = await notion.getPage(pageId);
return {
props: {
notionData: {
recordMap,
},
},
};
};
export async function getStaticPaths() {
return {
paths: [],
fallback: true,
};
}
export default function Page({
notionData,
}: {
notionData: {
recordMap: ExtendedRecordMap | null;
};
}) {
return <NotionPage notionData={notionData} />;
}
#2 Add email capture form to the page
Now that we have our Notion page content, we can add an email capture form to the page. We'll create a new component components/email-capture.tsx
and add the following code to it.
// components/email-capture.tsx
import { useEffect } from "react";
import { Button } from "@/components/ui/button";
export default function EmailCapture({
email,
setEmail,
onSubmitHandler,
isLoading,
}: {
email: string;
setEmail: React.Dispatch<React.SetStateAction<string>>;
onSubmitHandler: React.FormEventHandler<HTMLFormElement>;
isLoading: boolean;
}) {
return (
<>
<div className="flex h-screen flex-1 flex-col px-6 py-12 lg:px-8 bg-black">
<div className="sm:mx-auto sm:w-full sm:max-w-md">
<h2 className="mt-10 text-2xl font-bold leading-9 tracking-tight text-white">
Your action is requested to continue
</h2>
</div>
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-md">
<form className="space-y-4" onSubmit={onSubmitHandler}>
<div className="pb-5">
<div className="relative rounded-md shadow-sm space-y-2">
<label
htmlFor="email"
className="block text-sm font-medium leading-6 text-white">
Email address
</label>
<input
name="email"
id="email"
type="email"
autoComplete="email"
className="flex w-full rounded-md border-0 py-1.5 text-white bg-black shadow-sm ring-1 ring-inset ring-gray-600 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-gray-300 sm:text-sm sm:leading-6"
placeholder="Enter email"
onChange={(e) => {
setEmail(e.target.value);
}}
aria-invalid="true"
/>
<p className="text-sm text-gray-600">
This data will be shared with the owner of this Notion page.
</p>
</div>
</div>
<div className="flex justify-center">
<Button type="submit" className="w-1/3" loading={isLoading}>
Continue
</Button>
</div>
</form>
</div>
</div>
</>
);
}
// pages/index.tsx
// ...
export default function Page({
notionData,
}: {
notionData: {
recordMap: ExtendedRecordMap | null;
};
}) {
const [email, setEmail] = useState<string>("");
const [submitted, setSubmitted] = useState<boolean>(false);
const [isLoading, setIsLoading] = useState<boolean>(false);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setIsLoading(true);
const res = await fetch(`< API_ENDPOINT >`, {
method: "POST",
body: JSON.stringify({ email }),
});
setSubmitted(true);
setIsLoading(false);
};
// If email is not submitted, show the access form
if (!submitted) {
return (
<EmailCapture
email={email}
setEmail={setEmail}
onSubmitHandler={handleSubmit}
isLoading={isLoading}
/>
);
}
return (
<div className="bg-gray-950">
{submitted && notionData?.recordMap ? (
<NotionPage recordMap={notionData.recordMap} />
) : null}
</div>
);
}
Caveat: react-notion-x
works only with publicly accessible Notion pages. However, don't fret because the Notion page content is SSR'd on the server, so it's not publicly accessible and there's no place where the public Notion id is exposed in the client.
Conclusion
Amazing! You can now create an email-protected Notion page using Next.js and react-notion-x
. Start sharing your content with the world and collect leads and feedback from real people!
Thank you for reading. I am Marc, an open-source advocate. I am building papermark.io - the open-source alternative to DocSend.
Have fun building!
Help me out!
If you found this article helpful and got to understand react-notion-x, Next.js and SSR, I would be extremely glad if you could give us a star! And don't forget to share your thoughts in the comments โค๏ธ
Top comments (8)
As a notion fan, I'd give this one a try!
same! notion is amazing :)
Wouuuh, I might just give it a try! Thanks a lot ๐
Glad you find it useful ๐
Papermark to the ๐
To the ๐
Great article!
Thanks Nevo ๐ฅ