### Start Development Server Source: https://docs.indiekit.pro/setup/database Command to start your Indie Kit development server. This will typically trigger the application to connect to the database and begin serving. ```bash npm run dev ``` -------------------------------- ### Indie Kit Configuration File Example Source: https://docs.indiekit.pro/getting-started Example of the main configuration file (`src/lib/config.ts`). It allows customization of application name, authentication methods, and other features. This file is central to tailoring the app. ```typescript export const config = { appName: "Your App Name", auth: { enablePasswordAuth: true, }, // ... more options } ``` -------------------------------- ### Supabase PostgreSQL Connection String Example Source: https://docs.indiekit.pro/setup/database Example of a connection string for connecting to a Supabase PostgreSQL database. This string includes credentials and specific host information for Supabase projects. ```text postgresql://postgres:[YOUR-PASSWORD]@db.[project-ref].supabase.co:5432/postgres ``` -------------------------------- ### Neon PostgreSQL Connection String Example Source: https://docs.indiekit.pro/setup/database Example of a connection string for connecting to a Neon PostgreSQL database. This string typically includes the username, password, host, and database name. ```text postgresql://[user]:[password]@[neon-host]/[database] ``` -------------------------------- ### Clone Indie Kit Repository Source: https://docs.indiekit.pro/getting-started Clones the main Indie Kit repository or the B2B boilerplate. Ensure you have Git installed. This is the first step to getting the project on your local machine. ```bash git clone https://github.com/Indie-Kit/indie-kit cd indie-kit ``` ```bash git clone https://github.com/Indie-Kit/b2b-boilerplate ``` -------------------------------- ### Creating a Welcome Email Template Source: https://docs.indiekit.pro/setup/email A React component example for creating a welcome email template in Indie Kit. It demonstrates how to use layout components and dynamic props like userName and dashboardUrl. ```typescript export default function Welcome({ userName, dashboardUrl }: WelcomeEmailProps) { return ( Welcome to {appConfig.projectName}! 👋 Hi {userName}, We're excited to have you on board! Get started by visiting your dashboard: If you have any questions, just reply to this email - we're here to help! ); } ``` -------------------------------- ### Basic Component Usage in React Source: https://docs.indiekit.pro/customisation/installed-components A fundamental example of how to import and render a Hero1 component within a React functional component. This demonstrates the initial setup for using pre-installed components. ```javascript import Hero1 from "@/components/sections/hero-1"; export default function HomePage() { return (
); } ``` -------------------------------- ### PlanetScale MySQL Connection String Example Source: https://docs.indiekit.pro/setup/database Example of a connection string for connecting to a PlanetScale MySQL database, specifically for use with Prisma. It includes username, password, host, database name, and SSL acceptance. ```text mysql://[username]:[password]@[host]/[database]?sslaccept=strict ``` -------------------------------- ### Install Project Dependencies Source: https://docs.indiekit.pro/getting-started Installs all necessary project dependencies using pnpm. This command should be run after cloning the repository and installing pnpm. ```bash pnpm install ``` -------------------------------- ### Install Resend Dependency - Node.js Source: https://docs.indiekit.pro/setup/email/resend Installs the Resend package using pnpm, which is necessary for interacting with the Resend API for sending emails. ```shell pnpm add resend ``` -------------------------------- ### DNS Record Examples for Email Authentication Source: https://docs.indiekit.pro/setup/email Provides example DNS TXT records for DKIM, SPF, and DMARC, which are crucial for email authentication and deliverability. These records help verify your domain's legitimacy and prevent spoofing. ```plaintext selector._domainkey.yourdomain.com TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4..." yourdomain.com TXT "v=spf1 include:_spf.google.com include:_spf.mailgun.org ~all" _dmarc.yourdomain.com TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com" ``` -------------------------------- ### Test Mode Setup (Shell) Source: https://docs.indiekit.pro/payments/create-subscription Provides example environment variables for setting up Stripe's test mode. It includes placeholder values for Stripe secret and publishable keys, essential for secure testing of payment flows. ```shell STRIPE_SECRET_KEY="sk_test_..." STRIPE_PUBLISHABLE_KEY="pk_test_..." ``` -------------------------------- ### Example Blog Post Content Structure in MDX Source: https://docs.indiekit.pro/seo/create-blog-post This example demonstrates how to structure the content of a blog post using Markdown and MDX syntax within an `.mdx` file. It includes headings, subheadings, and placeholder text for content. ```mdx # Main Title ## Section 1 Content for section 1... ### Subsection 1.1 More detailed content... ## Section 2 Another section with content... ``` -------------------------------- ### Indie Kit Environment Variables Example Source: https://docs.indiekit.pro/getting-started Example of environment variables to be set in `.env.local` for local development. These include database credentials, authentication secrets, and API keys for services like Resend. Keep these sensitive variables out of your Git repository. ```dotenv # Database DATABASE_URL="your-database-url" # Auth NEXTAUTH_SECRET="your-secret-key" NEXTAUTH_URL="http://localhost:3000" # Email RESEND_API_KEY="your-api-key" # Payments (add when ready) # STRIPE_SECRET_KEY="your-stripe-key" ``` -------------------------------- ### Clear Pricing Display Example Source: https://docs.indiekit.pro/payments/create-one-time-payment Demonstrates a clear and concise way to present lifetime access pricing. Contrasts a good example with a bad one to emphasize clarity and avoid confusion for potential customers. ```html

$499 - Lifetime Access

One-time payment, no recurring fees

$499

``` -------------------------------- ### Install AWS SES SDK for Node.js Source: https://docs.indiekit.pro/setup/email/ses Installs the necessary AWS SDK v3 client for Amazon SES using pnpm. This is a prerequisite for sending emails via SES in a Node.js environment. ```bash pnpm add @aws-sdk/client-ses ``` -------------------------------- ### Common Cron Schedule Examples for Inngest Source: https://docs.indiekit.pro/background-jobs/running-scheduled-jobs Provides examples of common cron schedule expressions used for configuring recurring Inngest jobs. These examples cover daily, hourly, weekly, and monthly schedules, allowing for flexible job scheduling. ```json { "cron": "0 0 * * *" } // Daily at midnight { "cron": "0 */6 * * *" } // Every 6 hours { "cron": "0 9 * * 1" } // Mondays at 9 AM { "cron": "0 0 1 * *" } // First of month ``` -------------------------------- ### Configure PostgreSQL Drizzle Instance Source: https://docs.indiekit.pro/setup/database Example of how to configure the Drizzle ORM instance for PostgreSQL using the DATABASE_URL from environment variables. This is typically done in a db.ts file. ```typescript import { drizzle } from 'drizzle-orm/postgres-js'; export const db = drizzle(process.env.DATABASE_URL!) ``` -------------------------------- ### Protected Dashboard Page Example (Next.js) Source: https://docs.indiekit.pro/setup/auth/user-authentication This example demonstrates how to protect a dashboard page using the `auth()` function. It fetches the user session and then passes the user ID to a `DashboardMetrics` component, ensuring that only authenticated users can view dashboard data. ```javascript // src/app/(in-app)/dashboard/page.tsx import { auth } from '@/auth' import { DashboardMetrics } from '@/components/dashboard/metrics' export default async function DashboardPage() { const session = await auth() return (

Dashboard

) } ``` -------------------------------- ### Create Welcome Email Sequence (TypeScript) Source: https://docs.indiekit.pro/background-jobs/create-email-sequence Defines a welcome email sequence that triggers on user signup. It sends a series of emails over several days, including a welcome email, getting started tips, feature highlights, and an upgrade offer. Requires Inngest client and an email sending utility. ```typescript import { inngest } from "@/lib/inngest/client" import { sendEmail } from "@/lib/email/send" export const welcomeSequence = inngest.createFunction( { id: "welcome-sequence" }, { event: "user/signup" }, async ({ event, step }) => { const { email, name } = event.data // Day 0: Welcome (immediate) await step.run("send-welcome", async () => { await sendEmail({ to: email, template: "welcome", data: { name } }) }) // Day 1: Getting started tips await step.sleep("wait-1-day", "1d") await step.run("send-getting-started", async () => { await sendEmail({ to: email, template: "getting-started", data: { name } }) }) // Day 3: Feature highlights await step.sleep("wait-2-more-days", "2d") await step.run("send-features", async () => { await sendEmail({ to: email, template: "feature-highlights", data: { name } }) }) // Day 7: Upgrade offer await step.sleep("wait-4-more-days", "4d") await step.run("send-upgrade-offer", async () => { await sendEmail({ to: email, template: "upgrade-offer", data: { name } }) }) } ) ``` -------------------------------- ### Install IndieKit B2B Boilerplate Source: https://docs.indiekit.pro/multi-tenancy/b2b-kit Clone the IndieKit B2B boilerplate repository and install its dependencies using pnpm. ```bash git clone https://github.com/Indie-Kit/b2b-boilerplate cd b2b-boilerplate pnpm install ``` -------------------------------- ### TypeScript Example: Welcome Email Sequence with Inngest Source: https://docs.indiekit.pro/setup/background-jobs A TypeScript example demonstrating how to create a background job function using Inngest. This function sends a welcome email immediately, waits for 3 days, and then sends a follow-up email. ```typescript // src/lib/inngest/functions/welcome-email.ts import { inngest } from "../client"; import { sendEmail } from "@/lib/email"; export const welcomeEmailSequence = inngest.createFunction( { id: "welcome-email-sequence" }, { event: "user/signup" }, async ({ event, step }) => { // Send immediately await step.run("send-welcome", async () => { await sendEmail({ to: event.data.email, subject: "Welcome! 👋", template: "welcome" }); }); // Wait 3 days await step.sleep("wait-3-days", "3d"); // Send follow-up await step.run("send-tips", async () => { await sendEmail({ to: event.data.email, subject: "Here are some tips 💡", template: "tips" }); }); } ); ``` -------------------------------- ### Free Trial Transparency (HTML) Source: https://docs.indiekit.pro/payments/create-subscription Illustrates effective ways to communicate free trial terms. The good example explicitly states the trial duration, no credit card requirement, and easy cancellation, whereas the bad example is vague. ```html

No credit card required

Cancel anytime during trial

``` -------------------------------- ### Appropriate Redirection (Good vs. Bad) Source: https://docs.indiekit.pro/customisation/private-page Explains the importance of redirecting users to the correct location. The good example redirects unauthorized users to the login page, while the bad example redirects them to the home page, which can be confusing. ```javascript // ✅ Good - Redirect to login if (!user) redirect('/login') // ❌ Bad - Redirect to home (confusing) if (!user) redirect('/') ``` -------------------------------- ### Inngest Best Practice: Using Step Functions for Retryable Jobs Source: https://docs.indiekit.pro/background-jobs/running-scheduled-jobs Illustrates the recommended approach for using Inngest step functions to create retryable background jobs. The 'Good' example shows individual steps that can be retried automatically upon failure, while the 'Bad' example shows a monolithic block that would fail entirely. This relies on the Inngest SDK. ```javascript // ✅ Good - Retryable steps await step.run("fetch", async () => fetch()) await step.run("process", async () => process()) // ❌ Bad - All or nothing await fetch() await process() ``` -------------------------------- ### Component Installation Directory Structure Source: https://docs.indiekit.pro/customisation/components Illustrates the directory structure where new UI components are installed within the project. Components are placed in `src/components/ui/` and are user-owned. ```text src/ components/ ui/ calendar.tsx table.tsx chart.tsx ... ``` -------------------------------- ### TypeScript Example: Triggering a Background Job Source: https://docs.indiekit.pro/setup/background-jobs Example of how to trigger a background job in Inngest from your application code. This snippet shows sending a 'user/signup' event to initiate the welcome email sequence. ```typescript // When user signs up await inngest.send({ name: "user/signup", data: { email: user.email } }); ``` -------------------------------- ### Clear Pricing Display (HTML) Source: https://docs.indiekit.pro/payments/create-subscription Demonstrates good and bad practices for displaying pricing information to users. The good example clearly shows the price, billing frequency, and cancellation policy, while the bad example omits crucial details. ```html

$29/month

Billed monthly, cancel anytime

``` -------------------------------- ### Install shadcn/ui Component with pnpm Source: https://docs.indiekit.pro/customisation/components Demonstrates how to install a shadcn/ui component using the pnpm package manager within an Indie Kit project. This command downloads the component's code directly into your project. ```bash pnpm dlx shadcn@latest add [component-name] ``` -------------------------------- ### Inngest Best Practice: Clear Naming for Jobs and Events Source: https://docs.indiekit.pro/background-jobs/running-scheduled-jobs Demonstrates the importance of descriptive IDs for Inngest functions and events. Clear naming, as shown in the 'Good' examples, improves readability and maintainability. Vague names, shown in the 'Bad' examples, can lead to confusion. This applies to the configuration of Inngest functions. ```javascript // ✅ Good - Descriptive { id: "send-welcome-email-sequence" } { event: "user/signup.completed" } // ❌ Bad - Vague { id: "job1" } { event: "event" } ``` -------------------------------- ### Protected API Endpoint Examples Source: https://docs.indiekit.pro/customisation/api-calls These endpoints require authentication and are protected. They include examples for getting and updating user settings. ```APIDOC ## GET /api/app/user-settings ### Description Retrieves the current user's settings. Requires authentication. ### Method GET ### Endpoint /api/app/user-settings ### Parameters None (Authentication token is provided in headers) ### Request Example None ### Response #### Success Response (200) - **userId** (string) - The ID of the current user. - **theme** (string) - The user's preferred theme ('light' or 'dark'). - **notifications** (boolean) - Whether notifications are enabled for the user. #### Response Example ```json { "userId": "user-123", "theme": "light", "notifications": true } ``` ## POST /api/app/user-settings ### Description Updates the current user's settings. Requires authentication. ### Method POST ### Endpoint /api/app/user-settings ### Parameters #### Request Body - **theme** (string) - Required - The new theme to set for the user (e.g., 'dark'). ### Request Example ```json { "theme": "dark" } ``` ### Response #### Success Response (200) - **success** (boolean) - Indicates if the update was successful. #### Response Example ```json { "success": true } ``` ``` -------------------------------- ### Combining Multiple Components in React Source: https://docs.indiekit.pro/customisation/installed-components An example demonstrating how to compose a full landing page by importing and rendering multiple pre-installed components: Hero1, FeatureGrid, TestimonialGrid, and CTA1. ```javascript import Hero1 from "@/components/sections/hero-1"; import FeatureGrid from "@/components/sections/feature-grid"; import TestimonialGrid from "@/components/sections/testimonial-grid"; import CTA1 from "@/components/website/cta-1"; export default function LandingPage() { return (
); } ``` -------------------------------- ### Install Mailgun Dependencies with PNPM Source: https://docs.indiekit.pro/setup/email/mailgun Installs the `form-data` and `mailgun.js` packages required for Mailgun integration using the PNPM package manager. ```shell pnpm add form-data mailgun.js ``` -------------------------------- ### Install Mailchimp Transactional Client (pnpm) Source: https://docs.indiekit.pro/setup/email/mailchimp Installs the necessary Mailchimp transactional client package using pnpm. This is a prerequisite for sending emails via Mailchimp. ```bash pnpm add @mailchimp/mailchimp_transactional ``` -------------------------------- ### Deploy Landing Page with Git (Bash) Source: https://docs.indiekit.pro/launch-in-5-minutes This Bash script outlines the standard Git commands to stage, commit, and push changes to a remote repository, typically for deployment. It assumes the user has already cloned a repository and made necessary modifications. The commit message indicates the purpose of the changes, which is to launch the landing page. ```bash git add . git commit -m "Launch landing page" git push ``` -------------------------------- ### HTTP GET Request Handler for Fetching Data Source: https://docs.indiekit.pro/customisation/api-calls A basic example of an HTTP GET request handler, likely within an API route, that fetches data from a database and returns it as JSON. ```typescript export async function GET() { const data = await fetchFromDatabase() return NextResponse.json(data) } ``` -------------------------------- ### Set Up Database Schema with pnpm Source: https://docs.indiekit.pro/multi-tenancy/b2b-kit Push the database schema to your connected database using the pnpm command. ```bash pnpm db:push # Push schema to database ``` -------------------------------- ### Create Documentation File Structure Source: https://docs.indiekit.pro/docs This snippet shows the basic directory structure for adding new documentation pages. New MDX files should be placed within the `content/docs/` directory, and subdirectories can be used for organization. ```tree content/docs/ ├── getting-started.mdx ├── your-new-page.mdx └── advanced/ └── custom-feature.mdx ``` -------------------------------- ### Import Testimonial Components in React Source: https://docs.indiekit.pro/customisation/installed-components Provides import examples for various testimonial components like TestimonialGrid, Testimonial1, and Testimonial2. These are used to display social proof. ```javascript import TestimonialGrid from "@/components/sections/testimonial-grid"; import Testimonial1 from "@/components/sections/testimonial-1"; import Testimonial2 from "@/components/sections/testimonial-2"; ``` -------------------------------- ### Import and Use shadcn/ui Calendar Component Source: https://docs.indiekit.pro/customisation/components Shows how to import and render a Calendar component from shadcn/ui after installation. This example demonstrates basic usage with state management for selected dates. ```javascript import { Calendar } from "@/components/ui/calendar"; export default function MyPage() { return (
); } ``` -------------------------------- ### Example Quota Configurations Source: https://docs.indiekit.pro/setup/payments Illustrates different quota configurations for various subscription plans, including free, pro, and enterprise tiers. Unlimited resources are indicated by -1 for numerical quotas. ```javascript // Free Plan { canUseApp: true, projects: 3, apiCalls: 100, storage: "1GB" } // Pro Plan { canUseApp: true, projects: 100, apiCalls: 10000, storage: "100GB" } // Enterprise Plan { canUseApp: true, projects: -1, // -1 = unlimited apiCalls: -1, storage: "unlimited" } ``` -------------------------------- ### Add Date Picker with shadcn/ui Calendar Source: https://docs.indiekit.pro/customisation/components Provides a practical example of installing and using the shadcn/ui Calendar component for date selection. It includes state management for the selected date and basic styling. ```bash # Install calendar component pnpm dlx shadcn@latest add calendar ``` ```javascript "use client"; import { Calendar } from "@/components/ui/calendar"; import { useState } from "react"; export default function BookingPage() { const [date, setDate] = useState(new Date()); return (
); } ``` -------------------------------- ### Proactive Usage Limits Display (React) Source: https://docs.indiekit.pro/payments/current-plan-based-rendering Shows how to proactively display usage limits and provide timely upgrade prompts to users before they reach their quota. This example demonstrates showing current usage against the limit and suggesting an upgrade when nearing the threshold. ```javascript // ✅ Good - Show before they hit limit

Projects: {current}/{limit}

{current >= limit * 0.8 && (

Almost at limit! Consider upgrading.

)} // ❌ Bad - Only show after hitting limit ``` -------------------------------- ### Customizing Indie Kit Email Layout Source: https://docs.indiekit.pro/setup/email Example code snippet showing how to customize the color scheme for emails in Indie Kit by modifying the Tailwind CSS configuration within the layout component. ```typescript // src/emails/components/layout.tsx // ...Existing code... ``` -------------------------------- ### Set Up ngrok for Local Webhook Testing Source: https://docs.indiekit.pro/setup/payments/paypal Installs and uses ngrok to expose a local development server (running on port 3000) to the internet, enabling PayPal webhook testing during local development. Requires Node.js and npm. ```bash # Install ngrok (if not already installed) npm install -g ngrok # Start your Next.js app npm run dev # In another terminal, expose your local server ngrok http 3000 ``` -------------------------------- ### Copying Component File for Customization Source: https://docs.indiekit.pro/customisation/installed-components A command-line instruction demonstrating how to duplicate an existing component file (e.g., hero-1.tsx) to create a new custom variant (hero-custom.tsx). This is the first step for deeper code-level customization. ```bash # Copy an existing component cp src/components/sections/hero-1.tsx src/components/sections/hero-custom.tsx ``` -------------------------------- ### Add Data Table with shadcn/ui Table Source: https://docs.indiekit.pro/customisation/components Illustrates the installation and basic structure for implementing a data table using shadcn/ui's Table components. This example shows the necessary import statements and JSX structure for a table header and body. ```bash # Install table component pnpm dlx shadcn@latest add table ``` ```javascript import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; export default function UsersPage() { return ( Name Email Status John Doe john@example.com Active
); } ``` -------------------------------- ### Assemble Landing Page with Pre-built Components (Next.js) Source: https://docs.indiekit.pro/customisation/static-page This code snippet shows how to construct a landing page by combining various pre-built UI components within a Next.js application. It imports components like Hero, FeatureGrid, TestimonialGrid, and CTA, and renders them sequentially to create a complete page layout. This approach simplifies page creation and ensures a consistent design language. ```tsx import Hero1 from "@/components/sections/hero-1" import FeatureGrid from "@/components/sections/feature-grid" import TestimonialGrid from "@/components/sections/testimonial-grid" import CTA1 from "@/components/website/cta-1" export default function LandingPage() { return ( <> ) } ``` -------------------------------- ### Install pnpm Package Manager Source: https://docs.indiekit.pro/getting-started Installs pnpm globally, which is used for its speed and efficiency in managing project dependencies. This command requires npm to be installed. ```bash npm install -g pnpm ``` -------------------------------- ### Smart Onboarding Sequence with Conditional Logic (JavaScript) Source: https://docs.indiekit.pro/background-jobs/create-email-sequence This Inngest function implements a smart onboarding flow that sends a welcome email and then tailors subsequent emails based on the user's plan. It uses conditional logic to send either upgrade tips for free users or pro feature details for paid users after a day's delay. ```javascript export const smartOnboarding = inngest.createFunction( { id: "smart-onboarding" }, { event: "user/signup" }, async ({ event, step }) => { const { email, name, plan } = event.data // Welcome email await step.run("send-welcome", async () => { await sendEmail({ to: email, template: "welcome" }) }) await step.sleep("wait-1-day", "1d") // Different content based on plan if (plan === "free") { await step.run("send-upgrade-tips", async () => { await sendEmail({ to: email, template: "why-upgrade", data: { name } }) }) } else { await step.run("send-pro-features", async () => { await sendEmail({ to: email, template: "pro-features", data: { name } }) }) } } ) ``` -------------------------------- ### Create Protected API Endpoint (TypeScript) Source: https://docs.indiekit.pro/customisation/api-calls This snippet illustrates how to create protected API endpoints in Indie Kit that require authentication. It uses the `withAuthRequired` wrapper to ensure only authenticated users can access the endpoint. The example includes handlers for both GET requests to retrieve user settings and POST requests to update them, interacting with a database. ```typescript // src/app/api/app/user-settings/route.ts import { withAuthRequired } from '@/lib/auth/withAuthRequired' import { NextResponse } from 'next/server' import { db } from '@/db' import { users } from '@/db/schema' import { eq } from 'drizzle-orm' // GET user settings export const GET = withAuthRequired(async (req, context) => { const { session } = context // Fetch settings from database const userSettings = await db .select() .from(users) .where(eq(users.id, session.user.id)) .limit(1) return NextResponse.json({ userId: session.user.id, theme: userSettings[0]?.theme || 'light', notifications: userSettings[0]?.notifications || true, }) }) // POST to update settings export const POST = withAuthRequired(async (req, context) => { const { session } = context const body = await req.json() // Update in database await db .update(users) .set({ theme: body.theme }) .where(eq(users.id, session.user.id)) return NextResponse.json({ success: true }) }) ``` -------------------------------- ### Enable Sign-In and Generate Auth Secret Source: https://docs.indiekit.pro/launch-with-waitlist Configures environment variables to enable user sign-in and runs a command to set up secure authentication with encryption keys. Ensure NEXT_PUBLIC_SIGNIN_ENABLED is set to true and execute the auth secret command. ```bash # Add to your .env file NEXT_PUBLIC_SIGNIN_ENABLED=true # Run auth setup command npx auth secret ``` -------------------------------- ### HTTP Method: GET - Fetch Data Source: https://docs.indiekit.pro/customisation/api-calls Standard GET request for retrieving data from the server. ```APIDOC ## GET - Fetch Data ### Description Use the GET method to retrieve data from a specified resource. ### Method GET ### Endpoint Example: `/api/data` ### Parameters None (typically, query parameters may be used for filtering/sorting) ### Request Example None ### Response #### Success Response (200) - **data** - The requested data. #### Response Example ```javascript export async function GET() { const data = await fetchFromDatabase() return NextResponse.json(data) } ``` ``` -------------------------------- ### Graceful Degradation Example (React) Source: https://docs.indiekit.pro/payments/current-plan-based-rendering Illustrates the principle of graceful degradation by providing clear user feedback and an upgrade path when a feature is inaccessible due to plan limitations. It contrasts this with the 'bad' practice of simply hiding the feature without explanation. ```javascript // ✅ Good - Clear message with upgrade path if (!hasAccess) { return (

This feature requires Pro plan

) } // ❌ Bad - Just hiding with no explanation if (!hasAccess) return null ```