AI Prompts
Integrating Update with AI tools like Cursor, Lovable, Bolt, and Windsurf is a breeze. This page contains a collection of prompts that you can use to get started.
AI Prompts
Accelerate your Update integration using AI-assisted development
These carefully crafted prompts are designed to help you build with Update faster using AI coding assistants. Each prompt provides detailed context and requirements that help AI tools generate more accurate, production-ready code.
Benefits
- Complete Solutions: Each prompt is structured to generate full, working implementations
- Best Practices: Includes error handling, loading states, and edge cases
- Framework Aware: Adapts to your chosen framework (React, Next.js App Router, Pages Router)
- Production Ready: Generates TypeScript code with proper typing and modern patterns
How to Use
- Choose a prompt from the collection below
- Copy the entire prompt (including code examples)
- Using with AI Assistants:
- Cursor: Add as a project rule
- GitHub Copilot: Include with #{filename}
- Zed: Reference with /file
- Other IDEs: Paste directly into your chat
Available Prompts
Create New Project
Create a Next.js app with Update pre-configured.
Add to Existing Project
Integrate Update into an existing Next.js or React application.
Lock a Page
Add a paywall to restrict page access and protect premium content.
Smart Checkout Integration
Create checkout buttons that adapt to subscription status.
Create a Pricing Page
Build a pricing page with subscription plans and checkout.
Custom Authentication Components
Build login, logout, and user state management components.
Create New Project
Create a Next.js app with Update pre-configured. Set up authentication, billing, and all necessary dependencies in a single command.
// Prompt: Create a new Next.js project with Update pre-configured/*I want to create a new Next.js project with Update (https://update.dev) pre-configured.Update simplifies the integration of authentication and billing by wrapping providerslike Supabase for auth and Stripe for billing.Requirements:1. Project initialization with Update2. Environment variable setup3. Authentication configuration4. Billing integration5. Basic component structure6. TypeScript support*/// 1. Create new projectnpx create-update-app@latest my-appcd my-app// 2. Configure environment variables (.env.local)NEXT_PUBLIC_UPDATE_PUBLIC_KEY=your_update_api_keyNEXT_PUBLIC_SUPABASE_URL=your_supabase_urlNEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key// The project comes pre-configured with:// 3. Update Provider (app/layout.tsx)import { UpdateProvider } from '@update/react'export default function RootLayout({children}: {children: React.ReactNode}) {return (<UpdateProvider>{children}</UpdateProvider>)}// 4. Authentication Components (components/auth/sign-in.tsx)'use client';import { createClient } from '@/utils/update/client'export function SignIn() {async function handleSignIn() {const client = createClient()const { data, error } = await client.auth.signIn({redirect_url: window.location.origin + '/dashboard'})if (!error) {window.location.href = data.url}}return (<button onClick={handleSignIn}>Sign In</button>)}// 5. Protected Routes (middleware.ts)import { NextResponse } from 'next/server'import type { NextRequest } from 'next/server'import { updateSession } from '@/utils/update/middleware'export async function middleware(request: NextRequest) {return await updateSession(request)}export const config = {matcher: ['/protected/:path*']}// 6. Billing Components (components/billing/checkout.tsx)'use client';import { createClient } from '@/utils/update/client'export function Checkout({ priceId }: { priceId: string }) {async function handleCheckout() {const client = createClient()const { data, error } = await client.billing.createCheckoutSession(priceId,{ redirect_url: window.location.origin + '/success' })if (!error) {window.location.href = data.url}}return (<button onClick={handleCheckout}>Upgrade to Pro</button>)}/*Next Steps:1. Add your Update API key to .env.local2. Customize the authentication flow3. Set up your pricing plans in the Update dashboard4. Add protected routes for premium content5. Style your components*/
Add to Existing Project
Integrate Update into an existing Next.js or React application. Set up authentication, billing, and seamlessly migrate from direct Supabase usage.
// Prompt: Add Update Authentication to Your Existing Application/*I want to integrate Update (https://update.dev) into my existing Next.js application.Update works as a wrapper around Supabase authentication and billing.IMPORTANT: Before implementing this integration, I need to analyze how Supabase iscurrently implemented in my codebase, including client initialization patterns,authentication handling, and project structure.Prerequisites:1. An active Update account with API keys2. A configured Supabase project linked to my Update account3. My Supabase URL and anon key4. Existing Supabase implementation in my codebase*/// Implementation Steps// 1. Install required packagesnpm install @updatedev/js@^1.0.0 @updatedev/ssr@^1.0.0// 2. Configure environment variables in .env.localNEXT_PUBLIC_UPDATE_PUBLIC_KEY=your_update_public_keyNEXT_PUBLIC_SUPABASE_URL=your_supabase_urlNEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key// 3. Create client-side utility for browser contexts// utils/update/client.tsimport { createClient as createBrowserClient } from "@updatedev/js/supabase";export function createClient() {return createBrowserClient(process.env.NEXT_PUBLIC_UPDATE_PUBLIC_KEY!,process.env.NEXT_PUBLIC_SUPABASE_URL!,process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!);}// 4. Create server-side utility for Next.js// utils/update/server.tsimport { createClient as createServerClient } from "@updatedev/js/supabase";import { cookies } from "next/headers";export async function createClient() {const cookieStore = await cookies();return createServerClient(process.env.NEXT_PUBLIC_UPDATE_PUBLIC_KEY!,process.env.NEXT_PUBLIC_SUPABASE_URL!,process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,{cookies: {getAll() {return cookieStore.getAll();},setAll(cookiesToSet) {try {cookiesToSet.forEach(({ name, value, options }) =>cookieStore.set(name, value, options));} catch {// The setAll method was called from a Server Component.// This can be ignored if you have middleware refreshing// user sessions.}},},});}// 5. Add middleware for route protection// utils/update/middleware.tsimport { createClient as createMiddlewareClient } from "@updatedev/js/supabase";import { NextResponse, type NextRequest } from "next/server";export async function updateSession(request: NextRequest) {let response = NextResponse.next({request,});const client = createMiddlewareClient(process.env.NEXT_PUBLIC_UPDATE_PUBLIC_KEY!,process.env.NEXT_PUBLIC_SUPABASE_URL!,process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,{cookies: {getAll() {return request.cookies.getAll();},setAll(cookiesToSet) {cookiesToSet.forEach(({ name, value }) =>request.cookies.set(name, value));response = NextResponse.next({request,});cookiesToSet.forEach(({ name, value, options }) =>response.cookies.set(name, value, options));},},});// Optional: Check auth and redirect as neededconst { data } = await client.auth.getUser();const user = data?.user;// Example protected route logicif (!user && request.nextUrl.pathname.startsWith('/dashboard')) {return NextResponse.redirect(new URL('/login', request.url));}return response;}// middleware.ts (root directory)import { updateSession } from "./utils/update/middleware";import { NextRequest } from "next/server";export async function middleware(request: NextRequest) {return await updateSession(request);}export const config = {matcher: ["/((?!next/static|_next/image|favicon.ico|.\\.(?:svg|png|jpg|jpeg|gif|webp)$).)",],};// 6. Example usage: Authentication in client components// components/auth/LoginForm.tsx'use client';import { useState } from 'react';import { createClient } from '@/utils/update/client';export function LoginForm() {const [email, setEmail] = useState('');const [password, setPassword] = useState('');const [loading, setLoading] = useState(false);async function handleLogin(e) {e.preventDefault();setLoading(true);try {const client = createClient();const { error } = await client.auth.signInWithPassword({email,password,});if (error) throw error;// Redirect on successwindow.location.href = '/dashboard';} catch (err) {console.error('Login error:', err);alert('Login failed');} finally {setLoading(false);}}return (<form onSubmit={handleLogin}><inputtype="email"value={email}onChange={(e) => setEmail(e.target.value)}placeholder="Email"required/><inputtype="password"value={password}onChange={(e) => setPassword(e.target.value)}placeholder="Password"required/><button type="submit" disabled={loading}>{loading ? 'Logging in...' : 'Log In'}</button></form>);}// 7. Server component example// app/profile/page.tsximport { createClient } from '@/utils/update/server';export default async function ProfilePage() {const client = await createClient();const { data } = await client.auth.getUser();const user = data.user;if (!user) {return <div>Please log in to view your profile</div>;}return (<div><h1>Your Profile</h1><p>Email: {user.email}</p><p>User ID: {user.id}</p></div>);}// 8. Sign out function// components/auth/SignOutButton.tsx'use client';import { createClient } from '@/utils/update/client';export async function handleSignOut() {const client = createClient();await client.auth.signOut();// Redirect after sign outwindow.location.href = '/login';}// 9. Error handling best practicestry {const client = createClient();const { data, error } = await client.auth.signInWithPassword({email,password,});if (error) {// Handle specific error casesif (error.status === 400) {// Invalid credentials} else if (error.status === 429) {// Too many requests} else {// General error}console.error('Auth error:', error);return;}// Success path} catch (err) {// Unexpected errorsconsole.error('Unexpected error:', err);}/*Migration Tips:1. Replace all Supabase client initializations with Update client utilities2. Auth methods remain the same: client.auth.signUp(), client.auth.signInWithPassword()3. You gain additional billing methods: client.billing.*4. Update all import paths to use your new utility files5. Handle authentication errors properly with specific status code checksCommon issues to watch for:- Authentication state persistence requires correct middleware setup- Environment variables must be accessible in the correct context- Add exclamation marks (!) to env variables in TypeScript- Session cookies must be properly managed for state synchronizationNext Steps:1. Create protected routes/pages2. Implement subscription features with client.billing.* methods3. Add user profile managementFor full documentation, visit: https://update.dev/docs/auth/integration*/
Lock a Page
Add a paywall to restrict page access and protect premium content. Implement server-side protection, subscription checks, and preview content for non-subscribers.
// Prompt: Add a paywall to a Next.js application using Update/*I want to add a paywall to my Next.js application using Update (https://update.dev).I've already integrated Update's authentication, and now I need to restrict certaincontent to paid subscribers only.Requirements:1. Server-side route protection2. Server component subscription checks3. Client-side subscription verification4. Preview content for non-subscribers5. Upgrade flow integration6. Error handling and loading states*/// 1. Middleware Protection (middleware.ts)import { NextResponse } from 'next/server';import type { NextRequest } from 'next/server';import { createServerClient } from '@/utils/update/server';export async function middleware(request: NextRequest) {// Skip public routesif (request.nextUrl.pathname.startsWith('/public')) {return NextResponse.next();}const client = await createServerClient({ request });// Check subscription statusconst { data: subData, error: subError } = await client.billing.getSubscriptions();const hasActiveSubscription = subData?.subscriptions?.some((sub) => sub.status === "active");if (!hasActiveSubscription) {// Redirect to upgrade page or show previewreturn NextResponse.redirect(new URL('/upgrade', request.url));}return NextResponse.next();}export const config = {matcher: ['/protected/:path*']};// 2. Server Component (app/protected/page.tsx)import { createServerClient } from '@/utils/update/server';import { redirect } from 'next/navigation';export default async function ProtectedPage() {const client = await createServerClient();// Check subscription statusconst { data, error } = await client.billing.getSubscriptions();const hasActiveSubscription = data?.subscriptions?.some((sub) => sub.status === "active");if (!hasActiveSubscription) {redirect('/upgrade');}return (<div><h1>Protected Content</h1>{/* Your premium content here */}</div>);}// 3. Client Component with Preview (app/components/protected-content.tsx)'use client';import { useEffect, useState } from 'react';import { createClient } from '@/utils/update/client';export default function ProtectedContent() {const [status, setStatus] = useState<'loading' | 'subscribed' | 'preview'>('loading');const [error, setError] = useState<string | null>(null);useEffect(() => {async function checkSubscription() {try {const client = createClient();const { data, error } = await client.billing.getSubscriptions();if (error) throw error;const hasActiveSubscription = data.subscriptions.some((sub) => sub.status === "active");setStatus(hasActiveSubscription ? 'subscribed' : 'preview');} catch (err) {console.error('Failed to check subscription:', err);setError('Failed to verify subscription status');setStatus('preview');}}checkSubscription();}, []);if (error) {return (<div className="error-container"><p>{error}</p><button onClick={() => window.location.reload()}>Retry</button></div>);}if (status === 'loading') {return <div>Loading...</div>;}if (status === 'preview') {return (<div className="preview-container"><div className="preview-content">{/* Show limited preview content */}<h2>Preview Content</h2><p>Get a taste of what's available with a subscription...</p></div><div className="upgrade-prompt"><h3>Subscribe to Access Full Content</h3><buttononClick={async () => {const client = createClient();const { data } = await client.billing.createCheckoutSession('your_price_id',{ redirect_url: window.location.origin + '/success' });if (data?.url) {window.location.href = data.url;}}}>Upgrade Now</button></div></div>);}return (<div className="protected-content">{/* Your full premium content here */}<h1>Welcome to Premium Content</h1><p>Thank you for subscribing!</p></div>);}// 4. Styles (styles/protected-content.css).preview-container {border: 1px solid #e2e8f0;border-radius: 0.5rem;padding: 1.5rem;}.preview-content {opacity: 0.7;filter: blur(1px);pointer-events: none;}.upgrade-prompt {margin-top: 1.5rem;padding-top: 1.5rem;border-top: 1px solid #e2e8f0;text-align: center;}.upgrade-prompt button {background-color: #3b82f6;color: white;padding: 0.5rem 1rem;border-radius: 0.375rem;border: none;font-weight: 500;cursor: pointer;}.error-container {padding: 1rem;border: 1px solid #ef4444;border-radius: 0.375rem;color: #ef4444;}/*Implementation Notes:1. Replace 'your_price_id' with your actual Stripe price ID2. Customize the preview content and styling3. Add appropriate error messages and loading states4. Implement proper error boundaries5. Add analytics tracking for upgrade conversions*/
Smart Checkout Integration
Create smart checkout buttons that adapt based on subscription status. Handle different subscription states, manage upgrades/downgrades, and process payments seamlessly.
// Prompt: Create a smart checkout button with Update in Next.js/*I want to implement checkout functionality with Update (https://update.dev) in my Next.js application.I need to create a smart checkout button component that adapts based on subscription status andhandles various edge cases.*/Key Requirements:1. Subscription Status Handling:- Check current subscription status- Handle different subscription states (active, past_due, canceled)- Support multiple subscription tiers- Handle subscription changes (upgrades/downgrades)2. Error Handling:- Network errors during status check- Failed payments- Session timeouts- Rate limiting3. User Experience:- Loading states- Error messages- Success feedback- Subscription management4. Webhook Integration:- Handle subscription status changes- Process successful payments- Handle failed payments- Manage subscription lifecycleHere's the implementation:// 1. Smart Checkout Button Component'use client';import { useState, useEffect } from 'react';import { createClient } from '@/utils/update/client';interface SmartCheckoutButtonProps {priceId: string;productName?: string;buttonText?: string;buttonClassName?: string;redirectUrl?: string;onSuccess?: () => void;onError?: (error: Error) => void;}export function SmartCheckoutButton({priceId,productName = 'Premium',buttonText = 'Subscribe',buttonClassName = 'default-button-class',redirectUrl = typeof window !== 'undefined' ? window.location.origin + '/success' : '/success',onSuccess,onError}: SmartCheckoutButtonProps) {const [state, setState] = useState({isLoading: true,isCheckingOut: false,subscription: null,error: null});useEffect(() => {let mounted = true;async function checkSubscription() {try {const client = createClient();const { data, error } = await client.billing.getSubscriptions();if (error) throw error;if (!mounted) return;// Find matching subscriptionconst subscription = data.subscriptions.find(sub =>sub.price.id === priceId ||sub.product.id === priceId.split('_')[0]);setState(prev => ({...prev,subscription,isLoading: false}));} catch (err) {if (!mounted) return;setState(prev => ({...prev,error: err.message,isLoading: false}));onError?.(err);}}checkSubscription();return () => {mounted = false;};}, [priceId, onError]);async function handleCheckout() {setState(prev => ({ ...prev, isCheckingOut: true, error: null }));try {const client = createClient();const { data, error } = await client.billing.createCheckoutSession(priceId,{redirect_url: redirectUrl,allow_promotion_codes: true});if (error) throw error;// Store checkout session ID for verificationsessionStorage.setItem('checkoutSessionId', data.sessionId);// Redirect to checkoutwindow.location.href = data.url;} catch (err) {setState(prev => ({...prev,error: err.message,isCheckingOut: false}));onError?.(err);}}async function handleManageSubscription() {setState(prev => ({ ...prev, isCheckingOut: true, error: null }));try {const client = createClient();const { data, error } = await client.billing.createPortalSession({return_url: window.location.href});if (error) throw error;window.location.href = data.url;} catch (err) {setState(prev => ({...prev,error: err.message,isCheckingOut: false}));onError?.(err);}}if (state.isLoading) {return (<button disabled className={buttonClassName}><span className="loading-spinner" />Checking status...</button>);}if (state.error) {return (<div className="error-container"><buttononClick={() => setState(prev => ({ ...prev, error: null, isLoading: true }))}className={buttonClassName}>Retry</button><p className="error-message">{state.error}</p></div>);}const { subscription } = state;if (subscription?.status === 'active') {return (<buttononClick={handleManageSubscription}className={`${buttonClassName} current-plan`}disabled={state.isCheckingOut}>{state.isCheckingOut ? (<span className="loading-spinner">Processing...</span>) : ('Manage Subscription')}</button>);}if (subscription?.status === 'past_due') {return (<buttononClick={handleManageSubscription}className={`${buttonClassName} past-due`}disabled={state.isCheckingOut}>{state.isCheckingOut ? (<span className="loading-spinner">Processing...</span>) : ('Update Payment Method')}</button>);}if (subscription?.cancel_at_period_end) {return (<buttononClick={handleManageSubscription}className={`${buttonClassName} reactivate`}disabled={state.isCheckingOut}>{state.isCheckingOut ? (<span className="loading-spinner">Processing...</span>) : ('Reactivate Subscription')}</button>);}return (<buttononClick={handleCheckout}disabled={state.isCheckingOut}className={buttonClassName}>{state.isCheckingOut ? (<span className="loading-spinner">Processing...</span>) : (buttonText)}</button>);}// 2. Success Page Component (pages/success.tsx)'use client';import { useEffect, useState } from 'react';import { useSearchParams } from 'next/navigation';import { createClient } from '@/utils/update/client';export default function SuccessPage() {const [state, setState] = useState({isVerifying: true,error: null,subscription: null});const searchParams = useSearchParams();const sessionId = searchParams.get('session_id');useEffect(() => {async function verifyCheckout() {try {// Verify the checkout session matchesconst storedSessionId = sessionStorage.getItem('checkoutSessionId');if (sessionId !== storedSessionId) {throw new Error('Invalid checkout session');}const client = createClient();const { data, error } = await client.billing.getSubscriptions();if (error) throw error;setState({isVerifying: false,subscription: data.subscriptions[0],error: null});// Clear stored session IDsessionStorage.removeItem('checkoutSessionId');} catch (err) {setState({isVerifying: false,subscription: null,error: err.message});}}if (sessionId) {verifyCheckout();}}, [sessionId]);if (state.isVerifying) {return (<div className="success-page"><h1>Verifying your subscription...</h1><div className="loading-spinner" /></div>);}if (state.error) {return (<div className="success-page error"><h1>Subscription Error</h1><p>{state.error}</p><button onClick={() => window.location.href = '/pricing'}>Return to Pricing</button></div>);}return (<div className="success-page"><h1>Welcome to {state.subscription.product.name}!</h1><p>Your subscription is now active.</p><button onClick={() => window.location.href = '/dashboard'}>Go to Dashboard</button></div>);}// 3. Webhook Handler (app/api/webhooks/route.ts)import { headers } from 'next/headers';import { createServerClient } from '@/utils/update/server';export async function POST(request: Request) {const headersList = headers();const signature = headersList.get('stripe-signature');try {const client = await createServerClient();const event = await client.billing.constructWebhookEvent(await request.text(),signature);switch (event.type) {case 'customer.subscription.created':case 'customer.subscription.updated':// Handle subscription changesbreak;case 'customer.subscription.deleted':// Handle subscription cancellationbreak;case 'invoice.payment_failed':// Handle failed paymentsbreak;}return new Response(null, { status: 200 });} catch (err) {console.error('Webhook error:', err);return new Response(JSON.stringify({ error: err.message }),{ status: 400 });}}
Create a Pricing Page
Build a dynamic pricing page with subscription plans and checkout integration. Display pricing tiers, handle plan comparisons, and manage subscription lifecycles.
// Prompt: Build a dynamic pricing page with Update in Next.js/*I want to create a dynamic pricing page for my Next.js application using Update (https://update.dev).The page should fetch subscription plans from Update, display pricing information, and handlesubscription management.*/Key Requirements:1. Plan Display:- Fetch and display subscription plans- Show features for each plan- Highlight current plan- Support multiple billing intervals- Handle currency formatting2. User Experience:- Loading states- Error handling- Responsive design- Plan comparison- Feature tooltips3. Subscription Management:- New subscriptions- Plan changes- Cancellations- Reactivations- Payment updatesHere's the implementation:// 1. Pricing Page Component (app/pricing/page.tsx)import { createServerClient } from '@/utils/update/server';import { PricingTable } from './pricing-table';import { FeatureList } from './feature-list';export default async function PricingPage() {const client = await createServerClient();try {// Fetch pricing dataconst { data: pricingData, error: pricingError } = await client.billing.getProducts();if (pricingError) throw pricingError;// Fetch user's current subscriptionconst { data: subData, error: subError } = await client.billing.getSubscriptions();if (subError) throw subError;const currentSubscription = subData.subscriptions[0];return (<div className="pricing-container"><h1>Choose Your Plan</h1><PricingTableproducts={pricingData.products}currentSubscription={currentSubscription}/><FeatureList products={pricingData.products} /></div>);} catch (err) {return (<div className="error-container"><h1>Error Loading Pricing</h1><p>{err.message}</p><button onClick={() => window.location.reload()}>Try Again</button></div>);}}// 2. Pricing Table Component (app/pricing/pricing-table.tsx)'use client';import { useState } from 'react';import { SmartCheckoutButton } from '@/components/smart-checkout-button';interface PricingTableProps {products: Array<{id: string;name: string;description: string;prices: Array<{id: string;interval: 'month' | 'year';amount: number;currency: string;}>;features: string[];}>;currentSubscription?: {product_id: string;price_id: string;status: string;};}export function PricingTable({ products, currentSubscription }: PricingTableProps) {const [interval, setInterval] = useState<'month' | 'year'>('month');const formatPrice = (amount: number, currency: string) => {return new Intl.NumberFormat('en-US', {style: 'currency',currency: currency.toUpperCase(),minimumFractionDigits: 0}).format(amount / 100);};return (<div className="pricing-grid"><div className="interval-toggle"><buttononClick={() => setInterval('month')}className={interval === 'month' ? 'active' : ''}>Monthly</button><buttononClick={() => setInterval('year')}className={interval === 'year' ? 'active' : ''}>Yearly</button></div><div className="plans-grid">{products.map(product => {const price = product.prices.find(p => p.interval === interval);if (!price) return null;const isCurrentPlan = currentSubscription?.product_id === product.id;return (<divkey={product.id}className={`plan-card ${isCurrentPlan ? 'current' : ''}`}><h3>{product.name}</h3><p className="description">{product.description}</p><div className="price"><span className="amount">{formatPrice(price.amount, price.currency)}</span><span className="interval">/{interval}</span></div><ul className="features">{product.features.map((feature, index) => (<li key={index}>{feature}</li>))}</ul><SmartCheckoutButtonpriceId={price.id}productName={product.name}buttonText={isCurrentPlan ? 'Current Plan' : 'Choose Plan'}buttonClassName={`plan-button ${isCurrentPlan ? 'current' : ''}`}/></div>);})}</div></div>);}// 3. Feature List Component (app/pricing/feature-list.tsx)'use client';import { useState } from 'react';interface FeatureListProps {products: Array<{id: string;name: string;features: string[];}>;}export function FeatureList({ products }: FeatureListProps) {const [selectedFeature, setSelectedFeature] = useState<string | null>(null);// Collect all unique featuresconst allFeatures = Array.from(new Set(products.flatMap(product => product.features)));return (<div className="feature-comparison"><h2>Feature Comparison</h2><table className="comparison-table"><thead><tr><th>Feature</th>{products.map(product => (<th key={product.id}>{product.name}</th>))}</tr></thead><tbody>{allFeatures.map((feature, index) => (<trkey={index}className={selectedFeature === feature ? 'selected' : ''}onClick={() => setSelectedFeature(selectedFeature === feature ? null : feature)}><td>{feature}</td>{products.map(product => (<td key={product.id}>{product.features.includes(feature) ? '✓' : '—'}</td>))}</tr>))}</tbody></table></div>);}
Custom Authentication Components
Build login, logout, and user state management components. Create reusable authentication flows, handle social providers, and manage protected routes.
// Prompt: Create reusable authentication components with Update in Next.js/*I want to create reusable authentication components for my Next.js application usingUpdate (https://update.dev). I need components for sign-in, sign-out, user profile display,and protected route wrapper.*/Key Requirements:1. Authentication Components:- Sign-in form with email/password- Social authentication buttons- Sign-out button- User profile display- Protected route wrapper2. Error Handling:- Invalid credentials- Network errors- Session expiration- Rate limiting3. User Experience:- Loading states- Form validation- Error messages- Success feedback- Redirect handlingHere's the implementation:// 1. Sign In Form Component (components/auth/sign-in-form.tsx)'use client';import { useState } from 'react';import { createClient } from '@/utils/update/client';interface SignInFormProps {redirectUrl?: string;onSuccess?: () => void;onError?: (error: Error) => void;}export function SignInForm({redirectUrl = '/',onSuccess,onError}: SignInFormProps) {const [state, setState] = useState({email: '',password: '',isLoading: false,error: null});async function handleSubmit(e: React.FormEvent) {e.preventDefault();setState(prev => ({ ...prev, isLoading: true, error: null }));try {const client = createClient();const { error } = await client.auth.signInWithPassword({email: state.email,password: state.password});if (error) throw error;onSuccess?.();window.location.href = redirectUrl;} catch (err) {setState(prev => ({...prev,error: err.message,isLoading: false}));onError?.(err);}}return (<form onSubmit={handleSubmit} className="auth-form"><div className="form-group"><label htmlFor="email">Email</label><inputid="email"type="email"value={state.email}onChange={e => setState(prev => ({...prev,email: e.target.value}))}required/></div><div className="form-group"><label htmlFor="password">Password</label><inputid="password"type="password"value={state.password}onChange={e => setState(prev => ({...prev,password: e.target.value}))}required/></div>{state.error && (<div className="error-message">{state.error}</div>)}<buttontype="submit"disabled={state.isLoading}className="submit-button">{state.isLoading ? 'Signing in...' : 'Sign In'}</button></form>);}// 2. Social Auth Buttons (components/auth/social-auth.tsx)'use client';import { createClient } from '@/utils/update/client';interface SocialAuthProps {providers: Array<'google' | 'github'>;redirectUrl?: string;onError?: (error: Error) => void;}export function SocialAuth({providers,redirectUrl = '/',onError}: SocialAuthProps) {async function handleSocialAuth(provider: 'google' | 'github') {try {const client = createClient();const { error } = await client.auth.signInWithOAuth({provider,options: {redirectTo: redirectUrl}});if (error) throw error;} catch (err) {onError?.(err);}}return (<div className="social-auth">{providers.includes('google') && (<buttononClick={() => handleSocialAuth('google')}className="google-button">Continue with Google</button>)}{providers.includes('github') && (<buttononClick={() => handleSocialAuth('github')}className="github-button">Continue with GitHub</button>)}</div>);}// 3. User Profile Component (components/auth/user-profile.tsx)'use client';import { useEffect, useState } from 'react';import { createClient } from '@/utils/update/client';export function UserProfile() {const [state, setState] = useState({user: null,isLoading: true,error: null});useEffect(() => {async function loadProfile() {try {const client = createClient();const { data: { user }, error } = await client.auth.getUser();if (error) throw error;setState({user,isLoading: false,error: null});} catch (err) {setState({user: null,isLoading: false,error: err.message});}}loadProfile();}, []);if (state.isLoading) {return <div className="loading">Loading profile...</div>;}if (state.error) {return (<div className="error-message">Error loading profile: {state.error}</div>);}if (!state.user) {return <div>No user found</div>;}return (<div className="user-profile"><imgsrc={state.user.avatar_url || '/default-avatar.png'}alt="Profile"className="avatar"/><h2>{state.user.name || state.user.email}</h2><p>{state.user.email}</p></div>);}// 4. Protected Route Component (components/auth/protected-route.tsx)'use client';import { useEffect, useState } from 'react';import { createClient } from '@/utils/update/client';interface ProtectedRouteProps {children: React.ReactNode;fallback?: React.ReactNode;redirectTo?: string;}export function ProtectedRoute({children,fallback = <div>Loading...</div>,redirectTo = '/sign-in'}: ProtectedRouteProps) {const [state, setState] = useState({isAuthenticated: false,isLoading: true});useEffect(() => {let mounted = true;async function checkAuth() {try {const client = createClient();const { data: { user }, error } = await client.auth.getUser();if (!mounted) return;if (error || !user) {window.location.href = redirectTo;return;}setState({isAuthenticated: true,isLoading: false});} catch (err) {if (!mounted) return;window.location.href = redirectTo;}}checkAuth();const client = createClient();const { data: { subscription } } = client.auth.onAuthStateChange((event, session) => {if (!mounted) return;if (event === 'SIGNED_OUT') {window.location.href = redirectTo;}});return () => {mounted = false;subscription.unsubscribe();};}, [redirectTo]);if (state.isLoading) {return fallback;}if (!state.isAuthenticated) {return null;}return children;}