LogoStarterkitpro
Components

Lead

A component for capturing user email addresses and generating leads

A lead generation component that captures user email addresses for newsletters, updates, or marketing campaigns.

Lead component isn't included by default to keep StarterKitPro lean, as many SaaS applications don't require it. However, you can easily add it in under a minute when needed.

Lead Component

Configuration

Let's start Implementation

Lead Actions

actions/lead-actions.ts
"use server";
 
import { leadFormSchema, LeadFormSchema } from "@/lib/validation-schemas";
import { prisma } from "@/lib/db";
 
export const generateLead = async (data: LeadFormSchema) => {
  try {
    const validationResult = leadFormSchema.safeParse(data);
    if (!validationResult.success) {
      return {
        status: "error",
        errors: validationResult.error.flatten(),
      };
    }
 
    const validatedData = validationResult.data;
    await prisma.lead.create({
      data: {
        email: validatedData.email,
      },
    });
 
    return { status: "success" };
  } catch (error) {
    console.error("Error creating lead:", error);
    return { status: "error" };
  }
};

Lead Component

components/shared/lead.tsx
"use client";
 
import { Mail, SendHorizonal, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { toast } from "sonner";
import {
  Form,
  FormField,
  FormItem,
  FormControl,
  FormMessage,
} from "@/components/ui/form";
import { LeadFormSchema, leadFormSchema } from "@/lib/validation-schemas";
import { generateLead } from "@/actions/lead-actions";
 
export default function Lead() {
  const form = useForm<LeadFormSchema>({
    resolver: zodResolver(leadFormSchema),
    defaultValues: {
      email: "",
    },
  });
 
  const {
    handleSubmit,
    formState: { isSubmitting },
  } = form;
 
  const onSubmit = handleSubmit(async (data) => {
    const result = await generateLead(data);
    if (result.status === "success") {
      toast.success("Subscribed successfully.");
      form.reset();
    } else if (result.errors && result.errors.fieldErrors) {
      // Set field errors in the form if they exist
      Object.entries(result.errors.fieldErrors).forEach(([field, errors]) => {
        if (errors && errors.length > 0) {
          form.setError(field as any, {
            type: "server",
            message: errors[0],
          });
        }
      });
      toast.error("Please correct the errors in the form");
    } else {
      toast.error("Error while subscribing");
    }
  });
 
  return (
    <Form {...form}>
      <form onSubmit={onSubmit} className="mx-auto mt-10">
        <div className="bg-background has-[input:focus]:ring-muted relative grid grid-cols-[1fr_auto] items-center rounded-(--radius) border pr-3 shadow-sm shadow-zinc-950/5 has-[input:focus]:ring-2">
          <Mail className="text-caption text-secondary-foreground/50 pointer-events-none absolute inset-y-0 left-5 my-auto size-5" />
 
          <FormField
            control={form.control}
            name="email"
            render={({ field }) => (
              <FormItem className="w-full">
                <FormControl>
                  <input
                    placeholder="Your mail address"
                    className="h-14 w-full bg-transparent pl-12 focus:outline-hidden pr-3"
                    {...field}
                  />
                </FormControl>
              </FormItem>
            )}
          />
 
          <div className="md:pr-1.5 lg:pr-0">
            <Button
              type="submit"
              aria-label="submit"
              disabled={isSubmitting}
              className="rounded-(--radius)"
            >
              {isSubmitting && <Loader2 className="mr-2 size-4 animate-spin" />}
              <span className="hidden md:block">Get Started</span>
              <SendHorizonal
                className="relative mx-auto size-5 md:hidden"
                strokeWidth={2}
              />
            </Button>
          </div>
        </div>
        <FormField
          control={form.control}
          name="email"
          render={() => <FormMessage className="text-xs ml-2 mt-1" />}
        />
      </form>
    </Form>
  );
}

Validation Schema

lib/validation-schemas.ts
// add this in this file
export const leadFormSchema = z.object({
  email: z.string().email({ message: "Please enter a valid email address" }),
});
 
export type LeadFormSchema = z.infer<typeof leadFormSchema>;

Add Database Schema

prisma/schema.prisma
model Lead {
  id         String @id @default(auto()) @map("_id") @db.ObjectId
  email      String
  createdAt  DateTime @default(now())
 
  @@unique([email])
  @@map("leads")
}

Run Commands

Run npm run db:generate and npm run db:push to apply the changes to your database.

Now you can save the leads to database successfully

Usage

import { Lead } from "@/components/shared/lead";

Now you can use the component where you want like in Header or CTA

On this page