{"id":31572,"date":"2023-10-30T07:00:00","date_gmt":"2023-10-30T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=31572"},"modified":"2025-11-26T17:30:12","modified_gmt":"2025-11-27T01:30:12","slug":"authenticated-images-cloudinary-next-js-13-supabase-part-2","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2","title":{"rendered":"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)"},"content":{"rendered":"\n<p>In <a href=\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-1\" target=\"_blank\" rel=\"noreferrer noopener\">Part 1<\/a> of this two-part article, we\u2019ll set up Supabase, Cloudinary, and our backend for data consumption with security. In Part 2, we\u2019ll put everything together by looking at our Next.js 13 repository made for this article and its authentication concepts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getting Started<\/h2>\n\n\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>Ensure you have Node.js version 16.4 or above to make Next.js 13 work.<\/p>\n<\/div>\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/Fran-A-Dev\/cloudinary-next13\" target=\"_blank\" rel=\"noreferrer noopener\">Clone down my GitHub repository<\/a> locally. Once cloned, CD into the project directory.<\/li>\n\n\n\n<li>In your terminal, run these following commands:\n<ul class=\"wp-block-list\">\n<li><code>npm install<\/code><\/li>\n\n\n\n<li><code>npm install @supabase\/auth-helpers-nextjs @supabase\/supabase-js<\/code><\/li>\n\n\n\n<li><code>npm install -D encoding<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>Once you do that, create a <code>.env.local file<\/code> in the root of your project and add these keys and values, and paste the values in from your Supabase account accordingly.&nbsp;(These are the two values we saved from setting up Supabase in Part 1.)<\/li>\n\n\n\n<li><code>NEXT_PUBLIC_SUPABASE_URL=<br>NEXT_PUBLIC_SUPABASE_ANON_KEY=<\/code><\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Next Config File<\/h2>\n\n\n\n<p>In order to have Next.js optimize images from Cloudinary and use the server actions, which is currently experimental, we\u2019ll set these in our next.config.js file in the root of the project:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/** <span class=\"hljs-doctag\">@type <span class=\"hljs-type\">{import('next').NextConfig}<\/span> <\/span>*\/<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> nextConfig = {\n\n\u00a0\u00a0<span class=\"hljs-attr\">images<\/span>: {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">domains<\/span>: &#91;<span class=\"hljs-string\">\"res.cloudinary.com\"<\/span>],\n\n\u00a0\u00a0},\n\n\u00a0\u00a0<span class=\"hljs-attr\">experimental<\/span>: {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">serverActions<\/span>: <span class=\"hljs-literal\">true<\/span>,\n\n\u00a0\u00a0},\n\n};\n\n<span class=\"hljs-built_in\">module<\/span>.exports = nextConfig;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Next.js 13 Features and Paradigm Shift<\/h2>\n\n\n\n<p>Let\u2019s go do a high-level overview of the new features of Next.js 13 before we dive into the project.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">App Router<\/h3>\n\n\n\n<p>Next.js 13 introduced the new <strong>App Router<\/strong> built on top of <a href=\"https:\/\/beta.nextjs.org\/docs\/rendering\/server-and-client-components\" target=\"_blank\" rel=\"noreferrer noopener\">React Server Components<\/a> with support for layouts, nested routing, loading states, error handling, and more.<\/p>\n\n\n\n<p>Next.js 13 now gives you the ability to control component states very easily and at a granular level.<\/p>\n\n\n\n<p>Here\u2019s the component hierarchy in a folder:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764206724\/blog-Authenticated_Images_With_Cloudinary_Next.js_13_and_Supabase_Part_2-1.png\" alt=\"Component hierarchy in folder\"\/><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">Data Fetching<\/h3>\n\n\n\n<p>We used to use special functions like <code>getServerSideProps<\/code> for real-time data and <code>getStaticProps<\/code> for cached data before Next.js 13 was introduced.<\/p>\n\n\n\n<p>Next.js 13 has removed these functions and replaced them with these fetching formats that are <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Fetch_API\" target=\"_blank\" rel=\"noreferrer noopener\">built on top of the native fetch API<\/a>.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/nextjs.org\/docs\/app\/building-your-application\/data-fetching\/fetching-caching-and-revalidating#caching-data\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Fetch With Cached Data<\/strong><\/a><\/h4>\n\n\n\n<p>With cache data, Next.js will only do the actual HTTP request once, at the build time. The next time we load the page, Next.js will reply with the initial data and won\u2019t do the request again.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/This request would be cached until manually invalidated.<\/span>\n\n<span class=\"hljs-comment\">\/\/Similar to 'getStaticProps'<\/span>\n\nfetch(API_URL, { <span class=\"hljs-attr\">cache<\/span>: <span class=\"hljs-string\">'force-cache'<\/span> })\n\n<span class=\"hljs-comment\">\/\/Note :- Use this type of fetching for data that does not change often.<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/nextjs.org\/docs\/app\/building-your-application\/data-fetching\/fetching-caching-and-revalidating#caching-data\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Fetch With Dynamic Data<\/strong><\/a><\/h4>\n\n\n\n<p>When we want to run a new request for each page load, we can use the \u201cno-store\u201d value for the \u201ccache\u201d parameter.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ This request should be refetched on every request.<\/span>\n\n<span class=\"hljs-comment\">\/\/ Similar to 'getServerSideProps'<\/span>\n\nfetch(API_URL, { <span class=\"hljs-attr\">cache<\/span>: <span class=\"hljs-string\">'no-store'<\/span> })\n\n<span class=\"hljs-comment\">\/\/Note :- Use this for data that needs to be updated at each page load.<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\"><a href=\"https:\/\/nextjs.org\/docs\/app\/building-your-application\/data-fetching\/fetching-caching-and-revalidating#caching-data\" target=\"_blank\" rel=\"noreferrer noopener\"><strong>Fetch With Revalidating Data<\/strong><\/a><\/h4>\n\n\n\n<p>We can achieve a mix between Dynamic Data and Cached Data if we use the revalidate flag:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ This request should be cached with a lifetime of 10 seconds.<\/span>\n\n<span class=\"hljs-comment\">\/\/ Similar to 'getStaticProps' with the 'revalidate' option.<\/span>\n\nfetch(API_URL, { <span class=\"hljs-attr\">next<\/span>: { <span class=\"hljs-attr\">revalidate<\/span>: <span class=\"hljs-number\">10<\/span> } });\n\n<span class=\"hljs-comment\">\/\/Use this for the case when we have data that changes, but now very often.<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Below is a comparison for before Next.13 and after:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764206725\/blog-Authenticated_Images_With_Cloudinary_Next.js_13_and_Supabase_Part_2-2.png\" alt=\"A table comparing Before NextJs 13 and After NextJs 13\"\/><\/figure><\/div>\n\n\n<h2 class=\"wp-block-heading\">Server Rendering by Default<\/h2>\n\n\n\n<p><a href=\"https:\/\/nextjs.org\/docs\/app\/building-your-application\/rendering\/server-components\" target=\"_blank\" rel=\"noreferrer noopener\">Each component by default are server components<\/a>. The main benefits of this is the JS bundle is smaller and the rehydration process is quicker since there are fewer components to render on the client.<\/p>\n\n\n\n<p>The other benefit by defaulting and rendering the work on the server is the security aspect of it, which directly relates to our article. You can use access tokens, API keys, and any sensitive information on a server component and it won\u2019t be exposed in the client.<\/p>\n\n\n\n<p>You can combine both as well when interactivity is needed and using client components is a must. Here\u2019s a diagram per the Next.js docs:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764206727\/blog-Authenticated_Images_With_Cloudinary_Next.js_13_and_Supabase_Part_2-3.png\" alt=\"Diagram of composition methods from the Next.js docs\"\/><\/figure><\/div>\n\n\n<p>There are other features of Next.js 13 that we won\u2019t go over in this article, so <a href=\"https:\/\/nextjs.org\/docs\" target=\"_blank\" rel=\"noreferrer noopener\">please refer to their docs for a complete in depth overview.<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Authenticated Image Ticketing App<\/h2>\n\n\n\n<p>Now that we\u2019ve set up our repository, installed the necessary dependencies, and gone over the Next.js 13 features, let&#8217;s dive into the code and focus on a few files that make this app work with authentication.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Auth Form<\/h3>\n\n\n\n<p>The first file we want to discuss is the <code>AuthForm.jsx<\/code> file located at <code>app\/(auth)<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-string\">\"use client\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">AuthForm<\/span>(<span class=\"hljs-params\">{ handleSubmit }<\/span>) <\/span>{\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;email, setEmail] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;password, setPassword] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> handleSubmit(e, email, password)}&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Email:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"email\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> setEmail(e.target.value)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0value={email}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0required\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Password:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"password\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> setPassword(e.target.value)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0value={password}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0required\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"btn-primary\"<\/span>&gt;<\/span>Submit<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>At the top of the file, we have the Next.js 13 directive \u201cuse client,\u201d which tells Next.js that this will be a client-side component. In this case, we\u2019ll need to handle this component on the client, since we\u2019re rendering a form for email and password input. When the form is submitted, it handles the state using the React hook useState and the handleSubmit prop.<\/p>\n\n\n\n<p>Once we have this reusable component, we can now import and reuse this in our login and signup route segments. Because we\u2019re using Supbase as our backend, we have the createClientComponent function from that auth helper library specifically for Next.js 13, as you can see here at <code>app\/(auth)\/login\/page.jsx<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-string\">\"use client\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useRouter } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/navigation\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { createClientComponentClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@supabase\/auth-helpers-nextjs\"<\/span>;\n\n<span class=\"hljs-comment\">\/\/ components<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> AuthForm <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/AuthForm\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Login<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> router = useRouter();\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;error, setError] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> handleSubmit = <span class=\"hljs-keyword\">async<\/span> (e, email, password) =&gt; {\n\n\u00a0\u00a0\u00a0\u00a0e.preventDefault();\n\n\u00a0\u00a0\u00a0\u00a0setError(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> supabase = createClientComponentClient();\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> { error } = <span class=\"hljs-keyword\">await<\/span> supabase.auth.signInWithPassword({\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0email,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0password,\n\n\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (error) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setError(error.message);\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!error) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0router.push(<span class=\"hljs-string\">\"\/\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0};\n\n\u00a0\u00a0&#91;];\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-center\"<\/span>&gt;<\/span>Login<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">AuthForm<\/span> <span class=\"hljs-attr\">handleSubmit<\/span>=<span class=\"hljs-string\">{handleSubmit}<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{error &amp;&amp; <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"error\"<\/span>&gt;<\/span>{error}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>}\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The signup page is similar as well using the client side directive and handling the login and signup on the client with Supabase.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Dashboard<\/h3>\n\n\n\n<p>Let\u2019s turn our attention now to the dashboard folder. Within this folder, navigate to <code>(dashboard)\/create\/CreateForm.jsx<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> SubmitButton <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/app\/components\/SubmitButton\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { addTicket } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/actions\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">CreateForm<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">action<\/span>=<span class=\"hljs-string\">{addTicket}<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"w-1\/2\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Title:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">required<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"title\"<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Body:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">textarea<\/span> <span class=\"hljs-attr\">required<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"body\"<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Image URL:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">textarea<\/span> <span class=\"hljs-attr\">required<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"image\"<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>Priority:<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">select<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"priority\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"low\"<\/span>&gt;<\/span>Low Priority<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"medium\"<\/span>&gt;<\/span>Medium Priority<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"high\"<\/span>&gt;<\/span>High Priority<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">select<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">SubmitButton<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This is a form that allows permission to authenticated users to add content and image URL to the site and supabase backend. The feature that we want to focus on is the server components and the use of server side rendering. Traditionally, this would have been a client side component in Next.js, handling state and events on the browser, but notice that we have neither a useState hook nor do we have an event handler.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Instead, we have a prop called action and we pass in the addTicket function that comes from our actions.js file. The actions.js file has the directive \u201cuse server\u201d that tells Next.js to run this function on the server. So, we\u2019re utilizing a pure server component functionality in Next.js with a nested client component that comes from the SubmitButton.jsx file.<\/p>\n\n\n\n<p>This is where the benefits of rendering files, as we stated on the server, come with only the hydration and bundle we need in JavaScript on the client becomes minimal. Those are the keys we\u2019ll focus on in this article. There are a few other files and features in this repository that I encourage you to take a look at in the Next.js 13 documentation to gain more familiarity.&nbsp;&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Authentication is an important part of apps and sites. In this article, we used Cloudinary, Supabase, and Next.js 13 to create an app that allows authenticated users to register, log in, read authenticated content, and create content with images.<\/p>\n\n\n\n<p>Here\u2019s the complete Auth app in action that you should see when you run the `npm run` build then `npm run start` and copy an image URL from Cloudinary with some content into your Next.js 13\/Supabase app:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1764206730\/blog-Authenticated_Images_With_Cloudinary_Next.js_13_and_Supabase_Part_2-4.gif\" alt=\"GIF of the Next.js 13\/Supabase app\"\/><\/figure><\/div>\n\n\n<p>I hope you now have a better understanding of how you can combine Cloudinary with authentication. As always, I look forward to seeing what you build out there on the web! <a href=\"https:\/\/cloudinary.com\/users\/register_free\">Sign up<\/a> for a free Cloudinary account today to get started.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Part 1 of this two-part article, we\u2019ll set up Supabase, Cloudinary, and our backend for data consumption with security. In Part 2, we\u2019ll put everything together by looking at our Next.js 13 repository made for this article and its authentication concepts. Getting Started Next Config File In order to have Next.js optimize images from [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":31577,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,212],"class_list":["post-31572","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-image","tag-next-js"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)<\/title>\n<meta name=\"description\" content=\"Use Cloudinary, Supabase, and Next.js 13 to create an app that allows authenticated users to register, log in, and read and create content with images.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)\" \/>\n<meta property=\"og:description\" content=\"Use Cloudinary, Supabase, and Next.js 13 to create an app that allows authenticated users to register, log in, and read and create content with images.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-10-30T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-11-27T01:30:12+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)\",\"datePublished\":\"2023-10-30T14:00:00+00:00\",\"dateModified\":\"2025-11-27T01:30:12+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2\"},\"wordCount\":1063,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Image\",\"Next.js\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2023\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2\",\"url\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2\",\"name\":\"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA\",\"datePublished\":\"2023-10-30T14:00:00+00:00\",\"dateModified\":\"2025-11-27T01:30:12+00:00\",\"description\":\"Use Cloudinary, Supabase, and Next.js 13 to create an app that allows authenticated users to register, log in, and read and create content with images.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)","description":"Use Cloudinary, Supabase, and Next.js 13 to create an app that allows authenticated users to register, log in, and read and create content with images.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2","og_locale":"en_US","og_type":"article","og_title":"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)","og_description":"Use Cloudinary, Supabase, and Next.js 13 to create an app that allows authenticated users to register, log in, and read and create content with images.","og_url":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2","og_site_name":"Cloudinary Blog","article_published_time":"2023-10-30T14:00:00+00:00","article_modified_time":"2025-11-27T01:30:12+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)","datePublished":"2023-10-30T14:00:00+00:00","dateModified":"2025-11-27T01:30:12+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2"},"wordCount":1063,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA","keywords":["Guest Post","Image","Next.js"],"inLanguage":"en-US","copyrightYear":"2023","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2","url":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2","name":"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA","datePublished":"2023-10-30T14:00:00+00:00","dateModified":"2025-11-27T01:30:12+00:00","description":"Use Cloudinary, Supabase, and Next.js 13 to create an app that allows authenticated users to register, log in, and read and create content with images.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/authenticated-images-cloudinary-next-js-13-supabase-part-2#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Authenticated Images With Cloudinary, Next.js 13 and Supabase (Part 2)"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1697221980\/Blog-Authenticating_images_Part2\/Blog-Authenticating_images_Part2.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/31572","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=31572"}],"version-history":[{"count":10,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/31572\/revisions"}],"predecessor-version":[{"id":39437,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/31572\/revisions\/39437"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/31577"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=31572"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=31572"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=31572"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}