<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Julian Bustos</title>
    <description>The latest articles on Forem by Julian Bustos (@julimancan).</description>
    <link>https://forem.com/julimancan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F659942%2F97700b71-4c28-439e-8d78-8f0402269730.jpeg</url>
      <title>Forem: Julian Bustos</title>
      <link>https://forem.com/julimancan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/julimancan"/>
    <language>en</language>
    <item>
      <title>How to Integrate Google APIs in Your Next.js Project</title>
      <dc:creator>Julian Bustos</dc:creator>
      <pubDate>Tue, 15 Jul 2025 00:48:49 +0000</pubDate>
      <link>https://forem.com/julimancan/how-to-integrate-google-apis-in-your-nextjs-project-gi4</link>
      <guid>https://forem.com/julimancan/how-to-integrate-google-apis-in-your-nextjs-project-gi4</guid>
      <description>&lt;p&gt;Ever thought about using Google Docs or Sheets as more than just documents or spreadsheets? Imagine them as dynamic content sources or even simple databases for your applications. In this guide, we'll dive into how to set up Google's APIs to unlock these powerful services within your projects, transforming how you manage and access data.&lt;/p&gt;

&lt;p&gt;The specific version of Next.js you're using won't impact this guide, as our focus is solely on obtaining API keys from Google's platform.&lt;/p&gt;

&lt;p&gt;We will first need to create a new Service Account to do so go &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on Start Free&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplyhcyhfcm6ypf9qwxi5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplyhcyhfcm6ypf9qwxi5.png" alt="Start Service Account" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the terms of service and click &lt;strong&gt;Agree &amp;amp; Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create your Payment Profile (you won't be charged).&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Individual Profile Type&lt;/strong&gt; if you're not a business.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Now add a payment method.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Start Free&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select your Cloud Platform options.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Great! Now that we have a Service Account set up, look for the menu on the left and click on &lt;strong&gt;APIs &amp;amp; Services&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpxkefq3txja7l09wt9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpxkefq3txja7l09wt9p.png" alt="APIs &amp;amp; Services" width="765" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enable APIs &amp;amp; Services&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpz0ejwapze9iipagzcre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpz0ejwapze9iipagzcre.png" alt="Enable APIs &amp;amp; Services" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Search for Google Sheets API and select it&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft76fvzhrf7q9bae232l8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft76fvzhrf7q9bae232l8.png" alt="Google Sheets API" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Enable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Great now we have the google sheets API enabled now let's create the credentials so we can programmatically connect to it.&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Create Credentials&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qi5t3lc2ghdbohl9f6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qi5t3lc2ghdbohl9f6h.png" alt="Create Credentials" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Application Data &amp;amp; click &lt;strong&gt;Done&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlpnvegoay320xc0k7on.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlpnvegoay320xc0k7on.png" alt="Application Data" width="657" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click on the &lt;strong&gt;IAM &amp;amp; Admin&lt;/strong&gt; in the left menu and then click &lt;strong&gt;Service Accounts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2j6sr0cfiustr7gzp44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2j6sr0cfiustr7gzp44.png" alt="Service Accounts" width="430" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click on Create &lt;strong&gt;Service Account&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57eqshwqmc8kn1aaa117.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57eqshwqmc8kn1aaa117.png" alt="Create Service Account" width="647" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your account a name and a description and click &lt;strong&gt;Create And Continue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select Owner as the Role and click &lt;strong&gt;Continue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can click &lt;strong&gt;Done&lt;/strong&gt; on the final step.&lt;/p&gt;

&lt;p&gt;Now we can manage the keys for this account which will allow us to connect to Google Sheets.&lt;/p&gt;

&lt;p&gt;Click on the 3 dots and select &lt;strong&gt;Manage Keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F409sa1owbwcwpmi0gu5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F409sa1owbwcwpmi0gu5l.png" alt="Manage Keys" width="695" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Add Key&lt;/strong&gt; and then on &lt;strong&gt;Create new key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2r766de8xodt0adtv9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2r766de8xodt0adtv9x.png" alt="Create New Key" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;strong&gt;JSON&lt;/strong&gt; option and click &lt;strong&gt;Create&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq44r2cziei3rac1xi2ah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq44r2cziei3rac1xi2ah.png" alt="Create JSON Keys" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save it somewhere safe and then open it in your code editor&lt;/p&gt;

&lt;p&gt;Great all we really care about are the private_key and the client_email, these will allow us to establish the connection.&lt;/p&gt;

&lt;p&gt;Go back to your Nextjs Project and in the .env.local (if you don't have a  .env file yet create one in the root of your project) file let's create the environment variables we will need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env.local

GOOGLE_SERVICE_ACCOUNT_EMAIL=
GOOGLE_PRIVATE_KEY=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste your keys from the JSON file in here.&lt;/p&gt;

&lt;p&gt;Awesome! Now you are ready to start integrating google sheets like I did &lt;a href="https://dev.to/julimancan/use-nextjs-14-app-router-to-store-subscriber-info-in-google-sheets-for-free-4jea"&gt;here&lt;/a&gt; to store Newsletter subscribers from another nextjs app!&lt;/p&gt;

&lt;p&gt;Happy coding! 😊&lt;/p&gt;

</description>
      <category>googleapi</category>
      <category>coding</category>
      <category>nextjs</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Fastest way to start a new project with Nextjs &amp; Sanity CMS &amp; pnpm</title>
      <dc:creator>Julian Bustos</dc:creator>
      <pubDate>Tue, 10 Jun 2025 02:31:22 +0000</pubDate>
      <link>https://forem.com/julimancan/fastest-way-to-start-a-new-project-with-nextjs-sanity-cms-pnpm-2cpi</link>
      <guid>https://forem.com/julimancan/fastest-way-to-start-a-new-project-with-nextjs-sanity-cms-pnpm-2cpi</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Run
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm create next-app@latest my-new-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Select your nextjs options&lt;/li&gt;
&lt;li&gt;cd into your app
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd my-new-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a new sanity project, embed the studio and create all the config files, from the cli
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm dlx sanity@latest init --create-project "My New App" --dataset production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Select your configuration options for your sanity project as well as the route for the embedded studio.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Optional Steps
&lt;/h1&gt;

&lt;p&gt;Add the scripts to generate types from your schemas and queries, to your package.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "updateSchemaTypes": "sanity schema extract --enforce-required-fields",
    "generateQueryTypes": "sanity typegen generate",
    "generateTypes": "pnpm updateSchemaTypes &amp;amp;&amp;amp; pnpm generateQueryTypes",

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run pnpm generateTypes every time you update your schemas or add a new query using defineQuery function from next-sanity, and you can get typed queries.&lt;/p&gt;

&lt;p&gt;Now you're all setup and ready to go!&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>sanity</category>
      <category>sanitycms</category>
      <category>newproject</category>
    </item>
    <item>
      <title>Use Next.js 14 (App Router) to Store Subscriber Info in Google Sheets for FREE</title>
      <dc:creator>Julian Bustos</dc:creator>
      <pubDate>Fri, 20 Sep 2024 19:34:06 +0000</pubDate>
      <link>https://forem.com/julimancan/use-nextjs-14-app-router-to-store-subscriber-info-in-google-sheets-for-free-4jea</link>
      <guid>https://forem.com/julimancan/use-nextjs-14-app-router-to-store-subscriber-info-in-google-sheets-for-free-4jea</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;In this tutorial, we will learn how to store user input from a Next.js app into a Google Sheets spreadsheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Google Sheets Setup&lt;/li&gt;
&lt;li&gt;A running Next.js App&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will start with a clean project, but I assume you already have a Next.js app up and running. Below is what I have in my layout.tsx, globals.css, and home page files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/layout.tsx

import type { Metadata } from "next";
import "./globals.css";

export const metadata: Metadata = {
  title: "Nextjs Google Sheets Integration",
};

export default function RootLayout({
  children,
}: Readonly&amp;lt;{
  children: React.ReactNode;
}&amp;gt;) {
  return (
    &amp;lt;html lang="en"&amp;gt;
      &amp;lt;body
        className="antialiased"
      &amp;gt;
        {children}
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; /* app/global.css */

@tailwind base;
@tailwind components;
@tailwind utilities;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/page.tsx

export default function Home() {
  return (
    &amp;lt;main&amp;gt;
      &amp;lt;h1&amp;gt;Nextjs Google Sheets Integration&amp;lt;/h1&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's setup google so we can use it as our backend for our subscribers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Google Setup
&lt;/h2&gt;

&lt;p&gt;We will first need to create a new Service Account to do so go &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on Start Free&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplyhcyhfcm6ypf9qwxi5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplyhcyhfcm6ypf9qwxi5.png" alt="Start Service Account" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the terms of service and click &lt;strong&gt;Agree &amp;amp; Continue&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create your Payment Profile (you won't be charged).&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Individual Profile Type&lt;/strong&gt; if you're not a business.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Now add a payment method.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Start Free&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select your Cloud Platform options.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Great! Now that we have a Service Account set up, look for the menu on the left and click on &lt;strong&gt;APIs &amp;amp; Services&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpxkefq3txja7l09wt9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpxkefq3txja7l09wt9p.png" alt="APIs &amp;amp; Services" width="765" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enable APIs &amp;amp; Services&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpz0ejwapze9iipagzcre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpz0ejwapze9iipagzcre.png" alt="Enable APIs &amp;amp; Services" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Search for Google Sheets API and select it&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft76fvzhrf7q9bae232l8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft76fvzhrf7q9bae232l8.png" alt="Google Sheets API" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Enable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Great now we have the google sheets API enabled now let's create the credentials so we can programmatically connect to it.&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Create Credentials&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qi5t3lc2ghdbohl9f6h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qi5t3lc2ghdbohl9f6h.png" alt="Create Credentials" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Application Data &amp;amp; click &lt;strong&gt;Done&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlpnvegoay320xc0k7on.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzlpnvegoay320xc0k7on.png" alt="Application Data" width="657" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click on the &lt;strong&gt;IAM &amp;amp; Admin&lt;/strong&gt; in the left menu and then click &lt;strong&gt;Service Accounts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2j6sr0cfiustr7gzp44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2j6sr0cfiustr7gzp44.png" alt="Service Accounts" width="430" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now click on Create &lt;strong&gt;Service Account&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57eqshwqmc8kn1aaa117.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F57eqshwqmc8kn1aaa117.png" alt="Create Service Account" width="647" height="648"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Give your account a name and a description and click &lt;strong&gt;Create And Continue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Select Owner as the Role and click &lt;strong&gt;Continue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can click &lt;strong&gt;Done&lt;/strong&gt; on the final step.&lt;/p&gt;

&lt;p&gt;Now we can manage the keys for this account which will allow us to connect to Google Sheets.&lt;/p&gt;

&lt;p&gt;Click on the 3 dots and select &lt;strong&gt;Manage Keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F409sa1owbwcwpmi0gu5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F409sa1owbwcwpmi0gu5l.png" alt="Manage Keys" width="695" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;strong&gt;Add Key&lt;/strong&gt; and then on &lt;strong&gt;Create new key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2r766de8xodt0adtv9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk2r766de8xodt0adtv9x.png" alt="Create New Key" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select the &lt;strong&gt;JSON&lt;/strong&gt; option and click &lt;strong&gt;Create&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq44r2cziei3rac1xi2ah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq44r2cziei3rac1xi2ah.png" alt="Create JSON Keys" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save it somewhere safe and then open it in your code editor&lt;/p&gt;

&lt;p&gt;Great all we really care about are the private_key and the client_email, these will allow us to establish the connection.&lt;/p&gt;

&lt;p&gt;Go back to your Nextjs Project and in the .env.local (if you don't have a  .env file yet create one in the root of your project) file let's create the environment variables we will need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env.local

GOOGLE_SERVICE_ACCOUNT_EMAIL=
GOOGLE_PRIVATE_KEY=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste your keys from the JSON file in here.&lt;/p&gt;

&lt;p&gt;Awesome! Now we are ready to start writing some code finally!&lt;/p&gt;

&lt;p&gt;Let's run our app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And open it in the browser.&lt;/p&gt;

&lt;p&gt;We are going to create a button that opens a Dialog with the subscription form.&lt;/p&gt;

&lt;p&gt;Lets start by adding the SubscribeDialog component.&lt;/p&gt;

&lt;p&gt;Create Subscribe.tsx and import it in your page.tsx&lt;/p&gt;

&lt;p&gt;We'll copy some basic styles for the buttons from &lt;a href="https://v1.tailwindcss.com/components/buttons" rel="noopener noreferrer"&gt;tailwindcss.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will also use a dialog element so we can take advantage of some default behaviour that we'll see in action later, we have to give it the open property so we can see it on the screen, and some basic layout styles. &lt;/p&gt;

&lt;p&gt;If we remove the open prop you will see the content change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/Subscribe.tsx
const Subscribe = () =&amp;gt; {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;button className="bg-transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent rounded"&amp;gt;
        Subscribe
      &amp;lt;/button&amp;gt;

      &amp;lt;dialog open className="border border-black py-5 px-3"&amp;gt;
        &amp;lt;form className="flex flex-col gap-2"&amp;gt;
          &amp;lt;label htmlFor="email"&amp;gt;
            &amp;lt;span&amp;gt;Email: &amp;lt;/span&amp;gt;
            &amp;lt;input type="email" id="email" name="email" className="border border-black" /&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;button type="submit" className="transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent rounded"&amp;gt;Subscribe&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/dialog&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default Subscribe;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call it in page.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/page.tsx

import Subscribe from "./Subscribe";

export default function Home() {
  return (
    &amp;lt;main className="flex flex-col items-center justify-center min-h-[100dvh]"&amp;gt;
      &amp;lt;h1&amp;gt;Nextjs Google Sheets Integration&amp;lt;/h1&amp;gt;
      &amp;lt;Subscribe /&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can add the functionality to the button so we can open and close the dialog.&lt;/p&gt;

&lt;p&gt;We will need to use the useRef hook which means we will need to make this component into a client component. &lt;/p&gt;

&lt;p&gt;We will also write a toggleDialog function so we can open and close the dialog, and we will also add a close button to the dialog so we can close it. However because we are using the dialog and the showModal javascript function, it means we can use the ESC key to close the modal as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/Subscribe.tsx

"use client";

import { useRef } from "react";

const Subscribe = () =&amp;gt; {

  const dialogRef = useRef&amp;lt;HTMLDialogElement&amp;gt;(null);
  const toggleDialog = () =&amp;gt; {
    if (dialogRef.current?.open) {
      return dialogRef.current?.close();
    }
    dialogRef.current?.showModal();
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;button onClick={toggleDialog} className="bg-transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;
        Subscribe
      &amp;lt;/button&amp;gt;

      &amp;lt;dialog ref={dialogRef} className="border border-black py-10 px-3 backdrop:bg-black backdrop:opacity-70"&amp;gt;
        &amp;lt;form className="flex flex-col gap-2"&amp;gt;
          &amp;lt;button onClick={toggleDialog} className="absolute right-2 top-2"&amp;gt;X&amp;lt;/button&amp;gt;
          &amp;lt;label htmlFor="email"&amp;gt;
            &amp;lt;span&amp;gt;Email: &amp;lt;/span&amp;gt;
            &amp;lt;input type="email" id="email" name="email" className="border border-black" /&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;button type="submit" className="transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;Subscribe&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/dialog&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default Subscribe;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! But we also want to capture more than the users email, however in my case I just want first name and last name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.Subscribe.tsx

"use client";

import { useRef } from "react";

const Subscribe = () =&amp;gt; {

  const dialogRef = useRef&amp;lt;HTMLDialogElement&amp;gt;(null);
  const toggleDialog = () =&amp;gt; {
    if (dialogRef.current?.open) {
      return dialogRef.current?.close();
    }
    dialogRef.current?.showModal();
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;button onClick={toggleDialog} className="bg-transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;
        Subscribe
      &amp;lt;/button&amp;gt;

      &amp;lt;dialog ref={dialogRef} className="border border-black pt-10 pb-5 px-3 backdrop:bg-black backdrop:opacity-70"&amp;gt;
        &amp;lt;form className="flex flex-col gap-2"&amp;gt;
          &amp;lt;button onClick={toggleDialog} className="absolute right-2 top-2"&amp;gt;X&amp;lt;/button&amp;gt;
          &amp;lt;label htmlFor="email"&amp;gt;
            &amp;lt;span&amp;gt;Email: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="email" id="email" name="email" className="border border-black" /&amp;gt;
          &amp;lt;label htmlFor="firstName"&amp;gt;
            &amp;lt;span&amp;gt;First Name: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="firstName" id="firstName" name="firstName" className="border border-black" /&amp;gt;
          &amp;lt;label htmlFor="lastName"&amp;gt;
            &amp;lt;span&amp;gt;Last Name: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="lastName" id="lastName" name="lastName" className="border border-black" /&amp;gt;
          &amp;lt;button type="submit" className="transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;Subscribe&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/dialog&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default Subscribe;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now that we have a front end let's write a form action for us to safely store the user's information on the backend. We will use valibot to validate the user data and the google API package to store the data.&lt;/p&gt;

&lt;p&gt;Let's add the googleapis package first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm add googleapis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the action file, I'm going to call it subscribeAction, which will receive our form data and store it, so let's start by receiving the form data. To make it safe we will use a "use server" directive at the top of the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/subscribeAction.ts

"use server";

export const subscribeAction = async (formData: FormData) =&amp;gt; {
  const rawData = {
    email: formData.get("email"),
    firstName: formData.get("firstName"),
    lastName: formData.get("lastName"),
  }

  console.log({rawData})
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we have to call our function, in our form like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/Subscribe.ts
import { subscribeAction } from "./subscribeAction";

// ... rest of your code
        &amp;lt;form className="flex flex-col gap-2" action={subscribeAction}&amp;gt;
// ... rest of your code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you should be able to see the console log when you click the subscribe button.&lt;/p&gt;

&lt;p&gt;Awesome let's add valibot now so we can validate the data. (valibot is a smaller zod which will help you add types and validations and errors to show) valibot uses a slightly different approach than zod, if we want to run different validations on a single piece of data we need to create pipes, and then we can add any validations we need. Feel free to go to their &lt;a href="https://valibot.dev/" rel="noopener noreferrer"&gt;docs&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;Let's do that first&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pnpm add valibot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's best to write your validations in a separate file in case you need to call the types on the front-end. I'm going to call this file valibotSchemas.ts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/valibotSchemas.ts
import { email, maxLength, nonEmpty, object, pipe, string } from "valibot";

export const subscribeDataSchema = object({
  email: pipe(
    string(),
    nonEmpty("Please enter your email."),
    email("The email is badly formatted."),
    maxLength(30, "Your email is too long.")
  ),
  firstName: pipe( 
    string(),
    nonEmpty("Please enter your first name."),
    maxLength(30, "Your first name is too long.")
  ),
  lastName: pipe( 
    string(),
    nonEmpty("Please enter your last name."),
    maxLength(30, "Your first name is too long.")
  ),
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can import it so we can validate our data and get sweet type safety too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/subscribeAction.ts
"use server";

import { parse, ValiError } from "valibot";
import { subscribeDataSchema } from "./valibotSchemas";

export const subscribeAction = async (formData: FormData) =&amp;gt; {
  const rawData = {
    email: formData.get("email"),
    firstName: formData.get("firstName"),
    lastName: formData.get("lastName"),
  };
  try {
    const data = parse(subscribeDataSchema, rawData);
    console.log({ data });
  } catch (error: unknown) {
    if (error instanceof ValiError) {
      const issues = error.issues;
      const errorMessages = issues.map((issue) =&amp;gt; issue.message);
      console.log({ errorMessages });
    } else {
      console.log({ error });
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if you an empty value on your form and click subscribe and look at your console you will see the error messages, we will use these to let the user know if there is an issue. To do this we will use React's useFormState hook.&lt;/p&gt;

&lt;p&gt;We will add the useFOrmState and replace the action in our form in our Subscribe component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import { useRef } from "react";
import { subscribeAction } from "./subscribeAction";
import { useFormState } from "react-dom";

const Subscribe = () =&amp;gt; {
  const [formState, formAction] = useFormState(subscribeAction, {
    success: false,
    errors: null,
  });

  const dialogRef = useRef&amp;lt;HTMLDialogElement&amp;gt;(null);
  const toggleDialog = () =&amp;gt; {
    if (dialogRef.current?.open) {
      return dialogRef.current?.close();
    }
    dialogRef.current?.showModal();
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;button onClick={toggleDialog} className="bg-transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;
        Subscribe
      &amp;lt;/button&amp;gt;

      &amp;lt;dialog ref={dialogRef} className="border border-black pt-10 pb-5 px-3 backdrop:bg-black backdrop:opacity-70"&amp;gt;
        &amp;lt;form className="flex flex-col gap-2" action={formAction}&amp;gt;
          &amp;lt;button onClick={toggleDialog} className="absolute right-2 top-2"&amp;gt;X&amp;lt;/button&amp;gt;
          &amp;lt;label htmlFor="email"&amp;gt;
            &amp;lt;span&amp;gt;Email: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="email" id="email" name="email" className="border border-black" /&amp;gt;
          &amp;lt;label htmlFor="firstName"&amp;gt;
            &amp;lt;span&amp;gt;First Name: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="firstName" id="firstName" name="firstName" className="border border-black" /&amp;gt;
          &amp;lt;label htmlFor="lastName"&amp;gt;
            &amp;lt;span&amp;gt;Last Name: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="lastName" id="lastName" name="lastName" className="border border-black" /&amp;gt;
          &amp;lt;button type="submit" className="transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;Subscribe&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
      &amp;lt;/dialog&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default Subscribe;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now typescript is unhappy with out subscribe function because the action now needs to receive the formState, and also the returns of our function don't match the expected return defined in our form action ({ success: false, errors: null}) so let's add fix that.&lt;/p&gt;

&lt;p&gt;Since we are not going to use we can just leave it empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/subscribeAction.ts
"use server";

import { parse, ValiError } from "valibot";
import { subscribeDataSchema } from "./valibotSchemas";

export const subscribeAction = async (state: {}, formData: FormData) =&amp;gt; {
  const rawData = {
    email: formData.get("email"),
    firstName: formData.get("firstName"),
    lastName: formData.get("lastName"),
  };

  try {
    const data = parse(subscribeDataSchema, rawData);
    console.log({ data });

    return {
      success: true,
      errors: null,
    };
  } catch (error: unknown) {
    if (error instanceof ValiError) {
      const issues = error.issues;
      const errorMessages = issues.map((issue) =&amp;gt; issue.message);

      console.log({ errorMessages });

      return {
        success: false,
        errors: errorMessages,
      };
    } else {
      console.log({ error });
      return {
        success: false,
        errors: ["An error occurred."],
      };
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now on our Subscribe component let's render those messages if we have any.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/Subscribe.tsx
"use client";

import { useRef } from "react";
import { subscribeAction } from "./subscribeAction";
import { useFormState } from "react-dom";

const Subscribe = () =&amp;gt; {
  const [formState, formAction] = useFormState(subscribeAction, {
    success: false,
    errors: null,
  });

  const dialogRef = useRef&amp;lt;HTMLDialogElement&amp;gt;(null);
  const toggleDialog = () =&amp;gt; {
    if (dialogRef.current?.open) {
      return dialogRef.current?.close();
    }
    dialogRef.current?.showModal();
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;button onClick={toggleDialog} className="bg-transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;
        Subscribe
      &amp;lt;/button&amp;gt;

      &amp;lt;dialog ref={dialogRef} className="border border-black pt-10 pb-5 px-3 backdrop:bg-black backdrop:opacity-70"&amp;gt;
        &amp;lt;form className="flex flex-col gap-2" action={formAction}&amp;gt;
          &amp;lt;button onClick={toggleDialog} className="absolute right-2 top-2"&amp;gt;X&amp;lt;/button&amp;gt;
          &amp;lt;label htmlFor="email"&amp;gt;
            &amp;lt;span&amp;gt;Email: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="email" id="email" name="email" className="border border-black" /&amp;gt;
          &amp;lt;label htmlFor="firstName"&amp;gt;
            &amp;lt;span&amp;gt;First Name: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="firstName" id="firstName" name="firstName" className="border border-black" /&amp;gt;
          &amp;lt;label htmlFor="lastName"&amp;gt;
            &amp;lt;span&amp;gt;Last Name: &amp;lt;/span&amp;gt;
          &amp;lt;/label&amp;gt;
          &amp;lt;input type="lastName" id="lastName" name="lastName" className="border border-black" /&amp;gt;
          &amp;lt;button type="submit" className="transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;Subscribe&amp;lt;/button&amp;gt;
          {formState.errors?.map((error) =&amp;gt; (
            &amp;lt;span className="text-red-700"&amp;gt;
              *{error}
            &amp;lt;/span&amp;gt;
          ))}
        &amp;lt;/form&amp;gt;
      &amp;lt;/dialog&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default Subscribe;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now that we have dealt with the errors let's work on storing our data on a google spreadsheet. We have not yet created one so let's go ahead and do that. Go to &lt;a href="https://docs.google.com/spreadsheets/u/0/" rel="noopener noreferrer"&gt;Google Sheets&lt;/a&gt; and Create a new Blank Spreadsheet&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh78fgptx5ugjc6zhpw9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh78fgptx5ugjc6zhpw9n.png" alt="Create New Spreadsheet" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's add titles to the first row, in my case A will be email, B will be first name, and C will be last name.&lt;/p&gt;

&lt;p&gt;On the spreadsheet for ease of reading later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5vux9k5yx3jxjyj8cw0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa5vux9k5yx3jxjyj8cw0.png" alt="Spreadsheet titles" width="440" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, we will add the google Service Account email to the users allowed to edit the document. Click on &lt;strong&gt;Share&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ltmjg12594hx7s29iw8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ltmjg12594hx7s29iw8.png" alt="Click Share" width="800" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the Service Email we added to the .env.local and give it editor access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgc2l0jwryekhm6lo5ui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flgc2l0jwryekhm6lo5ui.png" alt="Editor Access" width="800" height="579"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Send&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will need this spreadsheet's id in order to connect to it.&lt;/p&gt;

&lt;p&gt;The id of the spreadsheet is in the URL, it is everything between /d/ and /edit rest of URL.&lt;/p&gt;

&lt;p&gt;..../spreadsheets/d/&amp;lt;&amp;gt;/edit......&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfgegydczopi2px347ri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfgegydczopi2px347ri.png" alt="Spreadsheet id location" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy it and let's head back to our .env.local and add it in there. I will call this environment variable GOOGLE_SPREADSHEET_ID. Remember to restart your server after adding anything to your .env.local.&lt;/p&gt;

&lt;p&gt;Now we can create a function that will return the google auth so we can use it in other places too.&lt;/p&gt;

&lt;p&gt;Let's create a new file for that, I will call it googleAuth.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/googleAuth.ts
import { google } from "googleapis";

export const googleAuth = async () =&amp;gt; {
  const clientEmail = process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL;
  const privateKey = process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, "\n");

  if (!clientEmail || !privateKey) {
    throw new Error("Missing Google credentials.");
  }

  return new google.auth.GoogleAuth({
    credentials: {
      client_email: clientEmail,
      private_key: privateKey,
    },
    scopes: [
      "https://www.googleapis.com/auth/drive",
      "https://www.googleapis.com/auth/drive.file",
      "https://www.googleapis.com/auth/spreadsheets",
    ],
  });
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great now let's update our action and finally store some data on our sheet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/subscribeAction.ts
"use server";

import { parse, ValiError } from "valibot";
import { subscribeDataSchema } from "./valibotSchemas";
import { googleAuth } from "./googleSpreadsheets";
import { google } from "googleapis";

export const subscribeAction = async (state: {}, formData: FormData) =&amp;gt; {

  const spreadsheetId = process.env.GOOGLE_SPREADSHEET_ID;

  if (!spreadsheetId) {
    return {
      success: false,
      errors: ["There was an error connecting to google."],
    };
  }

  const rawData = {
    email: formData.get("email"),
    firstName: formData.get("firstName"),
    lastName: formData.get("lastName"),
  };

  try {
    const data = parse(subscribeDataSchema, rawData);

    const sheets = await google.sheets({
      auth: await googleAuth(),
      version: "v4",
    });

    await sheets.spreadsheets.values.append({
      spreadsheetId,
      range: "A1:C1",
      valueInputOption: "USER_ENTERED",
      requestBody: {
        values: [[data.email, data.firstName, data.lastName]],
      },
    });

    return {
      success: true,
      errors: null,
    };
  } catch (error: unknown) {
    if (error instanceof ValiError) {
      const issues = error.issues;
      const errorMessages = issues.map((issue) =&amp;gt; issue.message);

      return {
        success: false,
        errors: errorMessages,
      };
    } else {
      console.error(error);
      return {
        success: false,
        errors: ["An error occurred."],
      };
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now check your spreadsheet and make sure you were able to add the information correctly. If you get any errors make sure your environment variables don't have any typos.&lt;/p&gt;

&lt;p&gt;If you get an unauthorized message you might have missed the sharing the spreadsheet with the service account step. Go back to the Google Setup section.&lt;/p&gt;

&lt;p&gt;Awesome, now that we are storing data, we want to make sure we don't add any repeats and that we get a success message if the information was added correctly.&lt;/p&gt;

&lt;p&gt;Let's work on the no email repeats part first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/subscribeAction.ts

"use server";

import { parse, ValiError } from "valibot";
import { subscribeDataSchema } from "./valibotSchemas";
import { googleAuth } from "./googleAuth";
import { google } from "googleapis";

export const subscribeAction = async (state: {}, formData: FormData) =&amp;gt; {

  const spreadsheetId = process.env.GOOGLE_SPREADSHEET_ID;

  if (!spreadsheetId) {
    return {
      success: false,
      errors: ["There was an error connecting to google."],
    };
  }

  const rawData = {
    email: formData.get("email"),
    firstName: formData.get("firstName"),
    lastName: formData.get("lastName"),
  };

  try {
    const data = parse(subscribeDataSchema, rawData);

    const sheets = await google.sheets({
      auth: await googleAuth(),
      version: "v4",
    });

    // Select spreadsheet the range to read my emails are on column A
    const readRange = "A1:A";

    // Get the emails from the spreadsheet
    const emails = await sheets.spreadsheets.values.get({
      spreadsheetId: process.env.GOOGLE_SPREADSHEET_ID,
      range: readRange,
    });

    // Check if the email already exists
    const emailExists = emails.data.values?.flat().includes(data.email);

    if (emailExists) {
      return {
        success: false,
        errors: ["You're already subscribed!"],
      };
    };

    await sheets.spreadsheets.values.append({
      spreadsheetId,
      range: "A1:C1",
      valueInputOption: "USER_ENTERED",
      requestBody: {
        values: [[data.email, data.firstName, data.lastName]],
      },
    });

    return {
      success: true,
      errors: null,
    };

  } catch (error: unknown) {

    if (error instanceof ValiError) {

      const issues = error.issues;
      const errorMessages = issues.map((issue) =&amp;gt; issue.message);

      return {
        success: false,
        errors: errorMessages,
      };

    } else {
      return {
        success: false,
        errors: ["An error occurred."],
      };
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great now we should be able to see a message if the user is already in our spreadsheet.&lt;/p&gt;

&lt;p&gt;Now let's add a small success message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app/Subscribe.tsx
"use client";

import { useRef } from "react";
import { subscribeAction } from "./subscribeAction";
import { useFormState } from "react-dom";

const Subscribe = () =&amp;gt; {
  const [formState, formAction] = useFormState(subscribeAction, {
    success: false,
    errors: null,
  });

  const dialogRef = useRef&amp;lt;HTMLDialogElement&amp;gt;(null);
  const toggleDialog = () =&amp;gt; {
    if (dialogRef.current?.open) {
      return dialogRef.current?.close();
    }
    dialogRef.current?.showModal();
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;button onClick={toggleDialog} className="bg-transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;
        Subscribe
      &amp;lt;/button&amp;gt;

      &amp;lt;dialog ref={dialogRef} className="border border-black pt-10 pb-5 px-3 backdrop:bg-black backdrop:opacity-70"&amp;gt;
        {formState.success ? (
          &amp;lt;div&amp;gt;
            &amp;lt;button onClick={toggleDialog} className="absolute right-2 top-2"&amp;gt;X&amp;lt;/button&amp;gt;
            &amp;lt;h2&amp;gt;Thank you for subscribing!&amp;lt;/h2&amp;gt;
          &amp;lt;/div&amp;gt;
        ) : (
          &amp;lt;form className="flex flex-col gap-2" action={formAction}&amp;gt;
            &amp;lt;button onClick={toggleDialog} className="absolute right-2 top-2"&amp;gt;X&amp;lt;/button&amp;gt;
            &amp;lt;label htmlFor="email"&amp;gt;
              &amp;lt;span&amp;gt;Email: &amp;lt;/span&amp;gt;
            &amp;lt;/label&amp;gt;
            &amp;lt;input type="email" id="email" name="email" className="border border-black" /&amp;gt;
            &amp;lt;label htmlFor="firstName"&amp;gt;
              &amp;lt;span&amp;gt;First Name: &amp;lt;/span&amp;gt;
            &amp;lt;/label&amp;gt;
            &amp;lt;input type="firstName" id="firstName" name="firstName" className="border border-black" /&amp;gt;
            &amp;lt;label htmlFor="lastName"&amp;gt;
              &amp;lt;span&amp;gt;Last Name: &amp;lt;/span&amp;gt;
            &amp;lt;/label&amp;gt;
            &amp;lt;input type="lastName" id="lastName" name="lastName" className="border border-black" /&amp;gt;
            &amp;lt;button type="submit" className="transparent hover:bg-green-500 text-green-700 font-semibold hover:text-white py-2 px-4 border border-green-500 hover:border-transparent"&amp;gt;Subscribe&amp;lt;/button&amp;gt;
            {formState.errors?.map((error) =&amp;gt; (
              &amp;lt;span className="text-red-700"&amp;gt;
                *{error}
              &amp;lt;/span&amp;gt;
            ))}
          &amp;lt;/form&amp;gt;
        )}
      &amp;lt;/dialog&amp;gt;
    &amp;lt;/&amp;gt;
  );
}

export default Subscribe;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like that, we're done! Now you know how to use Google Sheets to store data, which you could use as a lightweight CMS or even a simple database.&lt;/p&gt;

&lt;p&gt;I hope you learned something valuable from this tutorial. Feel free to leave any comments or ask any questions!&lt;/p&gt;

&lt;p&gt;Julian Bustos&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to Create a Custom Deploy (Vercel/Netlify) Button for your Sanity Project</title>
      <dc:creator>Julian Bustos</dc:creator>
      <pubDate>Thu, 05 Sep 2024 15:53:17 +0000</pubDate>
      <link>https://forem.com/julimancan/how-to-create-a-custom-deploy-vercelnetlify-button-for-your-sanity-project-9e6</link>
      <guid>https://forem.com/julimancan/how-to-create-a-custom-deploy-vercelnetlify-button-for-your-sanity-project-9e6</guid>
      <description>&lt;p&gt;Deploying a Sanity project to Vercel or Netlify has never been easier, thanks to their deploy hooks. But what if you want to take it a step further and create a custom deploy button that suits your unique project setup? In this guide, we’ll walk you through the process of creating a custom deploy button tailored specifically for your Sanity project, ensuring a seamless deployment experience for you and your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you will need
&lt;/h3&gt;

&lt;p&gt;Assuming you have a Next.js site with Sanity CMS v3 integrated, you’ll need a few essentials to create a custom deploy button. First, obtain a deploy hook URL from your deployment provider, which in this case is Vercel. This URL will be used to trigger the redeployment of your project whenever the button is clicked. Additionally, ensure you have a running Sanity CMS project, specifically version 3, as this guide will focus on building the custom button for this version of Sanity. With these components in place, you’ll be able to set up a streamlined deployment workflow tailored to your project.&lt;/p&gt;

&lt;p&gt;We will also need to add the &lt;a class="mentioned-user" href="https://dev.to/sanity"&gt;@sanity&lt;/a&gt;/ui library &lt;br&gt;
&lt;code&gt;&lt;br&gt;
npm install @sanity/ui &lt;br&gt;
&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Adding a Custom Deploy Button to the Sanity Dashboard
&lt;/h3&gt;

&lt;p&gt;The goal is to have a deploy button conveniently located on the top right corner of your Sanity dashboard. To achieve this, we’ll need to customize the Sanity navbar by creating a custom navigation component. Sanity allows us to extend the default UI, so we’ll make use of that flexibility to insert our deploy button.&lt;/p&gt;

&lt;p&gt;Let’s start by creating a custom navigation component. This component will be responsible for adding the button to the navbar, allowing users to trigger a deployment directly from within the dashboard. Here's how to get started with the custom navbar component.&lt;/p&gt;

&lt;p&gt;We'll first just render the custom sanity navbar as we just want to add to it.&lt;/p&gt;

&lt;p&gt;I like to keep my sanity code in one place and I have a directory called components. Inside of it we can create our file CustomNavbar.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sanity/components/CustomNavbar.tsx
import { NavbarProps } from "sanity";

export function CustomNavbar(props: NavbarProps) {
  const { renderDefault } = props;

  return (
    &amp;lt;&amp;gt;
      {renderDefault(props)}
    &amp;lt;/&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It won't work without implementing it, so let's import it in our sanity config. We can add to the studio dashboard custom components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sanityConfig.ts
import { CustomNavbar } from "./components/NewCustomNavbar";

export const sanityAdminConfig = {
  projectId: process.env.SANITY_PROJECT_ID || "",
  dataset,
  useCdn: process.env.NODE_ENV === "production",
  title: "Your Site Title",
  apiVersion: "2021-10-21",
  basePath: "/studio", // hosted studio path
  plugins: [
  ],
  schema: {
    // your schemas
  },
};

export const studioConfig = defineConfig({
  ...sanityAdminConfig,
  studio: {
    components: {
      navbar: CustomNavbar,
    },
  },
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! Now let's add a button to our nav, and let's add some sanity UI elements to our nav to make it look better.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Box, Button, Flex } from "@sanity/ui";
import { NavbarProps } from "sanity";

export function CustomNavbar(props: NavbarProps) {
  const { renderDefault } = props;

  return (
    &amp;lt;Flex align="center"&amp;gt;
      &amp;lt;Box flex={1}&amp;gt;
        {renderDefault(props)}
      &amp;lt;/Box&amp;gt;
      &amp;lt;Button
        tone="positive"
        mode="ghost"
        style={{
          height: "100%",
          borderRadius: 0,
        }}
      &amp;gt;
        Deploy
      &amp;lt;/Button&amp;gt;
    &amp;lt;/Flex&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great! Now let's prepare a function to trigger our deploy hook, and we can add sanity's toasts to give appropriate messages to the the user. &lt;/p&gt;

&lt;p&gt;Here we'll need to add our deploy hook, I stored it in my env as NEXT_PUBLIC_DEPLOY_HOOK.&lt;/p&gt;

&lt;p&gt;Since we are using the toast to give messages we will need to declare this function inside of our custom nav component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sanity/components/CustomNavbar.tsx
import { Box, Button, Flex, useToast } from "@sanity/ui";

export function CustomNavbar(props: NavbarProps) {
  // ... rest of your code
  const toast = useToast();
  const deployHookUrl = process.env.NEXT_PUBLIC_DEPLOY_HOOK;

  const deploySite = async () =&amp;gt; {

    if (!deployHookUrl) {
      toast.push({
        status: 'error',
        title: 'Deployment Failed',
        description: 'No Vercel deploy hook found. Please check your environment variables.',
      });
      return;
    }

    try {
      const response = await fetch(deployHookUrl, {
        method: 'POST',
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      toast.push({
        status: 'success',
        title: 'Deployment Triggered',
        description: 'Deployment has been triggered successfully. It may take a few minutes to complete.',
      });
    } catch (error) {
      console.error('Deployment failed:', error);
      toast.push({
        status: 'error',
        title: 'Deployment Failed',
        description: error instanceof Error ? error.message : 'An unknown error occurred',
      });
    }
  };
  return (
    &amp;lt;Flex align="center"&amp;gt;
      &amp;lt;Box flex={1}&amp;gt;
        {renderDefault(props)}
      &amp;lt;/Box&amp;gt;
      &amp;lt;Button
        tone="positive"
        mode="ghost"
        style={{
          height: "100%",
          borderRadius: 0,
        }}
      &amp;gt;
        Deploy
      &amp;lt;/Button&amp;gt;
    &amp;lt;/Flex&amp;gt;
  )
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great now lets prepare a handleDeploy function that we can call in our button. We will do this so we can trigger a timeout in the end and give our users some messages and enable and disable the button. We will also add a state to keep track of wether we are deploying or not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function CustomNavbar(props: NavbarProps) {
  // ... rest of your code
    const [isDeploying, setIsDeploying] = useState(false);

      const handleDeploy = async () =&amp;gt; {
    setIsDeploying(true);
    await deploySite();

    // Re-enable the button after 1 minute
    setTimeout(() =&amp;gt; {
      setIsDeploying(false);
    }, 60000); // 60000 ms = 1 minute
  };
  return (
    &amp;lt;Flex align="center"&amp;gt;
      &amp;lt;Box flex={1}&amp;gt;
        {renderDefault(props)}
      &amp;lt;/Box&amp;gt;
      &amp;lt;Button
        onClick={handleDeploy}
        disabled={isDeploying}

        tone="positive"
        mode="ghost"
        style={{
          height: "100%",
          borderRadius: 0,
        }}
      &amp;gt;
        {isDeploying ? 'Deploying...' : 'Deploy'}

      &amp;lt;/Button&amp;gt;
    &amp;lt;/Flex&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason we are using &lt;code&gt;setTimeout&lt;/code&gt; is that, unfortunately, there isn't a built-in way to listen for the completion of the build process directly. This workaround ensures that the deployment is triggered effectively from within your Sanity CMS project without relying on third-party applications.&lt;/p&gt;

&lt;p&gt;If you have any suggestions or methods for monitoring the build state changes more accurately, I would love to hear your thoughts or read about it.&lt;/p&gt;

&lt;p&gt;Thank you for reading, and I hope you found this guide useful for enhancing your deployment workflow.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to duplicate a Sanity Dataset for FREE</title>
      <dc:creator>Julian Bustos</dc:creator>
      <pubDate>Thu, 04 Apr 2024 17:56:21 +0000</pubDate>
      <link>https://forem.com/julimancan/how-to-duplicate-a-sanity-dataset-for-free-392i</link>
      <guid>https://forem.com/julimancan/how-to-duplicate-a-sanity-dataset-for-free-392i</guid>
      <description>&lt;p&gt;Updating a website's backend can be nerve-wracking, especially when the site is live. Introducing new features often requires code changes, and there's always a risk of something breaking unintentionally. This article explores a cost-effective way to avoid disrupting your Sanity CMS backed site during development by creating a separate development dataset.&lt;/p&gt;

&lt;p&gt;Have you ever screwed up your own site, because you were working on something else?&lt;/p&gt;

&lt;p&gt;I have a couple of times, and sometimes It's understandable because you are working on fixing it and no one is looking at it. Sometimes it's more important.&lt;/p&gt;

&lt;p&gt;I am at that point where it matters, my client wants to start updating the backend with the content, and I'm trying to write code to implement the latest features requested. &lt;/p&gt;

&lt;p&gt;An update that for a small bit of time would break the site. The good news is, it doesn't have to. If we split our datasets and we do our development on another dataset we can avoid issues like these. But I also want all the content that's already on the site.&lt;/p&gt;

&lt;p&gt;When you look up the Sanity documentation you'll come across a very handy option to copy the dataset into another dataset, but when you run it you this functionality requires a paid plan. However, you could do this for free too if we export the dataset and then import it into the new dataset, just a couple more steps. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check sanity version or install a the cli tool.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install --global sanity@latest

# Alternatively
yarn global add sanity@latest
pnpm install --global sanity@latest

# Running the CLI without global installation
npx -y sanity@latest [command]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Login to your Sanity account using the cli tool.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sanity login 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Remember to login with the correct user 😅.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setup your cli env. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the root of your project create a file called &lt;code&gt;sanity.cli.ts&lt;/code&gt; or &lt;code&gt;sanity.cli.js&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sanity.cli.ts
import { defineCliConfig } from "sanity/cli";

export default defineCliConfig({
  api: {
    projectId: "&amp;lt;your-project-id&amp;gt;",
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;cd into the root of your project&lt;/li&gt;
&lt;li&gt;Export your dataset
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sanity dataset export &amp;lt;dataset-name&amp;gt; &amp;lt;export-file.tar.gz&amp;gt; [--types &amp;lt;type1,type2,...&amp;gt;]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;dataset-name: Replace this with the actual name of the dataset you want to export (e.g., "production").&lt;/li&gt;
&lt;li&gt;export-file.tar.gz: Specify the desired filename and location for the exported data archive (e.g., "my-data.tar.gz").&lt;/li&gt;
&lt;li&gt;--types (optional): This flag allows you to export specific document types only. Separate multiple types with commas (e.g., "--types post, product").&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;in my case this means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sanity dataset export production production-file.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when its done we can now create the new dataset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sanity dataset create development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;select your choice, for me this is a public dataset as it's going to be used for development only.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;import your downloaded file into your dataset&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sanity dataset import [FILE | FOLDER | URL] [TARGET_DATASET]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for me that's going to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sanity dataset import production-file.tar.gz development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once done you have duplicated or copied your dataset for FREE! &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup a SANITY_DATASET environment variable in your env file.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//.env.local
SANITY_DATASET=development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update your sanity config file.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// sanity.config.ts
export const studioConfig = defineConfig({
  //...rest of your config
  dataset: process.env.SANITY_DATASET || "",
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you have a deployed version of the site you will want to update the environment variables of your deployed site.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You probably will want to delete that file from your project root, or store it somewhere else.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to add user auth with Clerk to Nextjs (App Directory) and store it in Embedded Sanity CMS</title>
      <dc:creator>Julian Bustos</dc:creator>
      <pubDate>Mon, 14 Aug 2023 18:32:34 +0000</pubDate>
      <link>https://forem.com/julimancan/how-to-add-user-auth-with-clerk-to-nextjs-app-directory-and-store-it-in-embedded-sanity-cms-f0</link>
      <guid>https://forem.com/julimancan/how-to-add-user-auth-with-clerk-to-nextjs-app-directory-and-store-it-in-embedded-sanity-cms-f0</guid>
      <description>&lt;h2&gt;
  
  
  How to add user auth with Clerk to Nextjs (App Directory) and store it in Sanity CMS
&lt;/h2&gt;

&lt;p&gt;First we are going to install a new Nextjs project but this should work with any Nextjs App Directory project, then we will install Clerk and get it working, once it’s working we will install and embed a Sanity studio and get it ready to receive the information provided by Clerk. We will finish by creating an API route, where we will send the user data to Sanity, and verify that the user was created.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install Nextjs in an empty folder.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;latest&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Proceed with you favourite settings I will choose Tailwind, Typescript, and the App Directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the purpose of this tutorial I want to start with an empty home page in &lt;code&gt;app/page.tsx&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid place-content-center min-h-screen"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Hello World
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run the project and verify everything is working
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="nx"&gt;dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install Clerk and dependencies.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;clerk&lt;/span&gt;&lt;span class="sr"&gt;/nextj&lt;/span&gt;&lt;span class="err"&gt;s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="http://www.clerk.com" rel="noopener noreferrer"&gt;www.clerk.com&lt;/a&gt; and sign-up for a free account.&lt;/li&gt;
&lt;li&gt;Once inside click on Add Application and give it a name&lt;/li&gt;
&lt;li&gt;Select your favorite sign-in providers. I will choose email and Google.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39qpou663gw449p2ertl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F39qpou663gw449p2ertl.png" alt="Image description" width="754" height="928"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy the API Keys and paste them into your .env.local file.&lt;/li&gt;
&lt;li&gt;Mount ClerkProvider
Update your root layout to include the  wrapper. The  component wraps your Next.js application to provide active session and user context to Clerk's hooks and other components. It is recommended that the  wraps the  to enable the context to be accessible anywhere within the app.&lt;/li&gt;
&lt;li&gt;Your app/layout.tsx should look something like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/layout.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./globals.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Inter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/font/google&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ClerkProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@clerk/nextjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Inter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;subsets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;latin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Create Next App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Generated by create next app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;RootLayout&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ReactNode&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClerkProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ClerkProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Protect your Application&lt;/p&gt;

&lt;p&gt;Now that Clerk is installed and mounted in your application, it’s time to decide which pages are public and which need to hide behind authentication. We do this by creating a &lt;code&gt;middleware.ts&lt;/code&gt; file at the root folder (or inside &lt;code&gt;src/&lt;/code&gt; if that is how you set up your app).&lt;/p&gt;

&lt;p&gt;In my case I will create the middleware.ts file inside the root folder of my project. I will also add a Public Routes array, so any route you would like to be public you will need to add to this array.&lt;/p&gt;

&lt;p&gt;We will add the studio route, and let sanity handle the auth for the CMS studio.&lt;br&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// middlewate.ts
import { authMiddleware } from "@clerk/nextjs";

// This example protects all routes including api/trpc routes
// Please edit this to allow other routes to be public as needed.
// See https://clerk.com/docs/nextjs/middleware for more information about configuring your middleware

const publicRoutes = ["/", "/studio"];

export default authMiddleware({
  publicRoutes,
});

export const config = {
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create the sign-up page.&lt;/p&gt;

&lt;p&gt;In the app directory create a sign-up folder and then another catch-all [[…sign-up]] folder with a page inside of it. Your route should look like this: &lt;code&gt;app/sign-up/[[…sign-up]]/page.tsx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Inside the page.tsx file we will add the provided SignUp component from Clerk.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/sign-up/[[…sign-up]]/page.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SignUp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@clerk/nextjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid place-content-center min-h-screen"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SignUp&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do the same for the Sign in page. But we will also add the redirectUrl parameter provided by nextjs so that the user can be redirected back to the page they were visiting before or home if there isn’t any.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/sign-up/[[…sign-in]]/page.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SignIn&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@clerk/nextjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid place-content-center min-h-screen"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SignIn&lt;/span&gt; &lt;span class="na"&gt;redirectUrl&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;redirectUrl&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Next we will add some more environment variables to control the behavior of clerk and also to tell it what route the sign-up and sign-in pages are in, we do this in the .env.local file in the root right after our API keys.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .env.local&lt;/span&gt;

&lt;span class="nx"&gt;NEXT_PUBLIC_CLERK_SIGN_IN_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/sign-i&lt;/span&gt;&lt;span class="err"&gt;n
&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_CLERK_SIGN_UP_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/sign-u&lt;/span&gt;&lt;span class="err"&gt;p
&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a link to the sign up page inside the homepage. In &lt;code&gt;app/page.tsx&lt;/code&gt; lets add the link&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the app, navigate to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; using the browser and click on sign-up, sign-up for your app.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid place-content-center min-h-screen"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/sign-up"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign Up&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fecxy1ai1agqgezqjj05m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fecxy1ai1agqgezqjj05m.png" alt="Image description" width="800" height="981"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="http://dashboard.clerk.com" rel="noopener noreferrer"&gt;dashboard.clerk.com&lt;/a&gt; select your project and verify your new user.&lt;/li&gt;
&lt;li&gt;In the homepage let’s add some user data to the home page and some conditional rendering to see the data if the user is logged in.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/page.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@clerk/nextjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid place-content-center gap-2 min-h-screen"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserButton&lt;/span&gt; &lt;span class="na"&gt;afterSignOutUrl&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome back &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid place-content-center min-h-screen"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/sign-up"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign Up&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Let’s visit our homepage:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftaykpof8cq4jhp9ac9cz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftaykpof8cq4jhp9ac9cz.png" alt="Image description" width="800" height="872"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we click on the user image we have access to some options, for now I will try to sign out to see the other view.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqn9hwp09z7zasy5205vx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqn9hwp09z7zasy5205vx.png" alt="Image description" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You should see your sign-up button now:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6dtuqabv0oq96k8imr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6dtuqabv0oq96k8imr9.png" alt="Image description" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We can also add a signout button provided by clerk to make it easier to sign out and clearer that its a sign up / sign in page as well:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//// app/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserButton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SignOutButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@clerk/nextjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid place-content-center gap-2 min-h-screen"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex items-center gap-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserButton&lt;/span&gt; &lt;span class="na"&gt;afterSignOutUrl&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome back &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SignOutButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid place-content-center min-h-screen"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/sign-up"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign Up / Sign In&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now that we have sign up and sign out flows setup let’s create and embed a new sanity CMS in our Nextjs project, this process will still work even if the CMS isn’t embedded however I will embed a new project for the purpose of this tutorial.&lt;/li&gt;
&lt;li&gt;Install the necessary packages for Sanity to work as well as the client packages to make the requests to the CMS.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="nx"&gt;sanity&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sanity&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sanity&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;portabletext&lt;/span&gt;&lt;span class="sr"&gt;/react @sanity/&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;sanity&lt;/span&gt;&lt;span class="sr"&gt;/image-ur&lt;/span&gt;&lt;span class="err"&gt;l
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="http://sanity.io" rel="noopener noreferrer"&gt;sanity.io&lt;/a&gt; sign up for an account. Sanity gives instructions on how to create the studio however since we are embedding the studio on a Nextjs project we can just ignore them and navigate to &lt;code&gt;https://www.[sanity.io/manage](http://sanity.io/manage)&lt;/code&gt;. If Sanity created a project for you click on it and copy the project ID, if they didn’t you can click on Create a new project on the top and then copy the project ID provided.&lt;/li&gt;
&lt;li&gt;Go to your .env.local and add the project id under the Clerk variables like this:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// .env.local&lt;/span&gt;
&lt;span class="nx"&gt;SANITY_PROJECT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;sanity&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create a sanity folder on the root of your project, and a config.ts file with the following content:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sanity/config.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sanityConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SANITY_PROJECT_ID&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;useCdn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2021-03-25&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create your studioConfig.ts file inside the sanity folder in it for now we will define the config and the basePath for the sanity studio.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sanity/studioConfig.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sanityConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sanity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sanityAdminConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sanityConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clerk Users and Sanity Test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/admin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;studioConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sanityAdminConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Inside App directory create a page studio route and an index catch all filder, where we will mount the studio. &lt;code&gt;app/studio/[[…index]]/page.tsx&lt;/code&gt; We will import the studio config we just created and give it as a prop to the NextStudio component provided by Sanity, this is Client component so we will have to add the use client directive at the top of the file.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/studio/[[…index]]/page.tsx&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;studioConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/sanity/studioConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;NextStudio&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-sanity/studio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;StudioPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//  Supports the same props as `import {Studio} from 'sanity'`, `config` is required&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NextStudio&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;studioConfig&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Lets restart our project by running &lt;code&gt;npm run dev&lt;/code&gt; and navigate to &lt;code&gt;http://localhost:3000/studio&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We should get this error:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpq9utmlggteommodre8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpq9utmlggteommodre8v.png" alt="Image description" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is because we need to allow the route in the Sanity studio for it to be able to read and write to sanity’s database. We can add CORS origins in the Sanity Management Dashboard for the project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://www.sanity.io/manage" rel="noopener noreferrer"&gt;https://www.sanity.io/manage&lt;/a&gt; open the project we created previously and navigate to the API tab and look for the + Add CORS origin button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fisp8hciszewyv51achop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fisp8hciszewyv51achop.png" alt="Image description" width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add: &lt;code&gt;http://localhost:3000&lt;/code&gt;, and click Allow Credentials since we are embedding the studio in our NextJS project we need to allow it to have credentials. When you deploy your site you will need to add the root address of your site ie: &lt;code&gt;[http://www.yoursite.com](http://www.yoursite.com)&lt;/code&gt; to the list of allowed sites.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fam93gx0lux0qfnskvcyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fam93gx0lux0qfnskvcyp.png" alt="Image description" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click Save.&lt;/li&gt;
&lt;li&gt;We can also delete &lt;a href="http://localhost:3333" rel="noopener noreferrer"&gt;localhost:3333&lt;/a&gt; since this is the location of the default sanity studio which we are not using.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgwbzvvwtznf058fkalhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgwbzvvwtznf058fkalhj.png" alt="Image description" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go back to &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;`http://localhost:3000&lt;/a&gt;/studio` and sign in with your user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3li4xafbos4e0khq7j7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3li4xafbos4e0khq7j7.png" alt="Image description" width="800" height="868"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have an empty studio embedded in the Nextjs site! Let’s add a users document list to our sanity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the schema for the users, for our tutorial we will just need first name, last name, and email.&lt;/li&gt;
&lt;li&gt;Create a schemas folder inside the sanity folder and create user.ts &lt;code&gt;sanity/schemas/user.ts&lt;/code&gt;  add the document for users, we will use an icon from sanity so that the users’ list gets a nice icon.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sanity/schemas/user.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defineField&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sanity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserIcon&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@sanity/icons&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineType&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;document&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserIcon&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;defineField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firstName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;First Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;defineField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lastName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Last Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nf"&gt;defineField&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now lets add the schemas to our studioConfig. I like to keep my schemas organized in a separate file so that it’s easy to add or remove schemas from my project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create in the sanity folder a schemas.ts file. &lt;code&gt;sanity/schema.ts&lt;/code&gt; and add the following:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sanity/schema.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userSchema&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./schemas/user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schemaTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;userSchema&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Import the schemas into the &lt;code&gt;sanity/studioConfig.ts&lt;/code&gt; file and add it to the config.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sanity/studioConfig.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sanityConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sanity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schemaTypes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./schemas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sanityAdminConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sanityConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clerk Users and Sanity Test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/studio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schemaTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;studioConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sanityAdminConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are still getting the same screen, that’s because I forgot to add the desktool to the config, let’s do that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sanity/studioConfig.ts &lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sanityConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sanity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schemaTypes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./schemas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;deskTool&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sanity/desk&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sanityAdminConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sanityConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clerk Users and Sanity Test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/studio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;deskTool&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schemaTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;studioConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sanityAdminConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Refresh, and we should be able to see the users list and we can add users to our list,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23w45n3eg12g5btgkkti.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F23w45n3eg12g5btgkkti.png" alt="Image description" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, we want them to be added when the user signs in with our site. To do that we will use Clerks environment variables to redirect the user after sign-up to an API route, where we can get the user’s information add save it in our sanity and redirect the user again either to where they where coming from or to the homepage.&lt;/p&gt;

&lt;p&gt;Let’s start by creating the API route first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a folder create-sanity-user (you can name this something relevant to your app) inside the app folder and create a file route.tsx. This is a special Nextjs file that let’s us create api end points. &lt;code&gt;app/create-sanity-user/route.ts&lt;/code&gt;, in it we will use clerk’s &lt;code&gt;currentUser&lt;/code&gt; server function to access the user information. We also want to prevent anyone from accessing the route, so if the user doesn’t exist then we can redirect them to the sign-in page, however since we are using the middleware.ts file we won’t need to add that to our api route. Let’s try to return the user’s data first.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/create-sanity-user/route.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@clerk/nextjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Navigate to &lt;a href="http://localhost:3000/create-sanity-user" rel="noopener noreferrer"&gt;&lt;code&gt;http://localhost:3000/create-sanity-user&lt;/code&gt;&lt;/a&gt; we should now see the data that clerk gives you access too, we can save any of it in our sanity.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;[http://localhost:3000/](http://localhost:3000/)&lt;/code&gt; and log out and navigate back to &lt;a href="http://localhost:3000/create-sanity-user" rel="noopener noreferrer"&gt;&lt;code&gt;http://localhost:3000/create-sanity-user&lt;/code&gt;&lt;/a&gt;` and we should now get redirected to the sign-in page! Great!&lt;/li&gt;
&lt;li&gt;Let’s log back in and we should get redirected back to create-sanity-user end point.&lt;/li&gt;
&lt;li&gt;Let’s now add the user data to sanity, we need to create a sanityClient file which will allow us to have access to sanity’s functionality, and be able to read and write data from our nextjs app.&lt;/li&gt;
&lt;li&gt;We will also need an API token from sanity to be able to write data onto it. Go back to &lt;code&gt;https://www.[sanity.io/manage](http://sanity.io/manage)&lt;/code&gt; and in the API tab click on + Add API token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fit9ecqjtdjrs3vxn48vg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fit9ecqjtdjrs3vxn48vg.png" alt="Image description" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give it a name and select the Editor option to have access to read and write token options. Copy your token, and add it to your &lt;code&gt;.env.local&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;jsx&lt;br&gt;
SANITY_API_TOKEN=(your api token)&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create in the sanity folder a &lt;code&gt;sanityClient.ts&lt;/code&gt;  file and import the createClient function from next-sanity, and your own config, and just add the token from the environment variables, or empty if it doesn’t exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`jsx&lt;br&gt;
// sanity/sanityClient.ts&lt;/p&gt;

&lt;p&gt;import { createClient } from "next-sanity";&lt;br&gt;
import { sanityConfig } from "./config";&lt;br&gt;
export default createClient({&lt;br&gt;
  ...sanityConfig,&lt;br&gt;
  token: process.env.SANITY_API_TOKEN || "",&lt;br&gt;
});&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;code&gt;app/create-sanity-user/route.ts&lt;/code&gt; file and import the sanityClient we just created, we will use the createIfNotExists (which prevents errors when a user already exists) function that the next-sanity package provides to create a document in our sanity.&lt;/li&gt;
&lt;li&gt;We are also going to get the base path URL to redirect to from the request, this will work for both development and deployed, since the NextResponse.redirect function expects a full url. And redirect the user to this base URL.&lt;/li&gt;
&lt;li&gt;Even though the middleware is taking care of authentication, we still want to prevent typescript errors in case the user doesn’t exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`jsx&lt;br&gt;
// app/create-sanity-user/route.ts&lt;/p&gt;

&lt;p&gt;import { NextApiRequest } from "next";&lt;br&gt;
import sanityClient from "@/sanity/sanityClient";&lt;br&gt;
import { currentUser } from "@clerk/nextjs";&lt;br&gt;
import { NextResponse } from "next/server";&lt;/p&gt;

&lt;p&gt;export const GET = async (req: NextApiRequest) =&amp;gt; {&lt;br&gt;
  const user = await currentUser();&lt;br&gt;
  if (!user) {&lt;br&gt;
    return NextResponse.redirect("/sign-in");&lt;br&gt;
  }&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const {id, firstName, lastName, emailAddresses} = user;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;await sanityClient.createIfNotExists({&lt;br&gt;
    _type: "user",&lt;br&gt;
    _id: id,&lt;br&gt;
    firstName,&lt;br&gt;
    lastName,&lt;br&gt;
    email: emailAddresses[0].emailAddress&lt;br&gt;
  })&lt;/p&gt;

&lt;p&gt;const url = req.url?.split("/create-sanity-user")[0]  || "/"&lt;br&gt;
  return NextResponse.redirect(url);&lt;br&gt;
};&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restart your server and  navigate to &lt;a href="http://localhost:3000/create-sanity-user" rel="noopener noreferrer"&gt;&lt;code&gt;http://localhost:3000/create-sanity-user&lt;/code&gt;&lt;/a&gt; then navigate to your studio at &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;`http://localhost:3000&lt;/a&gt;/studio` and see the new user has been added to your studio&lt;/li&gt;
&lt;li&gt;The last step will be to update the &lt;code&gt;NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL&lt;/code&gt; environment variable and point it to /create-sanity-user route to our environment variables so Clerk can have the right behavior after signing-up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;jsx&lt;br&gt;
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/create-sanity-user&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Restart your server since we updated the environment variables and try to signup with a different user, then check your studio and verify the new user was added.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it, now you can add your own specific details to your users, or relate it to other documents specific for your use case.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
