Cards
CTA Card
A responsive CTA card with an image, text, and an email subscription form.
Installation
CLI
npx shadcn@latest add https://satisui.xyz/r/cta-card.jsonManual
No external dependencies are required. Copy and paste the following code into components/satisui/cta-card.tsx.
'use client';
import { cn } from '@/lib/utils';
import Image from 'next/image';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { useState } from 'react';
interface CTACardProps {
/** The main headline or title for the CTA. */
title: string;
/** A short description supporting the title. */
description: string;
/** The source URL for the image. */
imageSrc: string;
/** Alt text for the image, for accessibility. */
imageAlt?: string;
/** Placeholder text for the email input field. */
inputPlaceholder?: string;
/** Text displayed on the submission button. */
buttonText?: string;
/** Callback function executed when the form is submitted. */
onSubmit: (email: string) => void;
/** Optional additional class names to apply to the root card element for custom styling. */
className?: string;
}
/**
* A responsive Call-to-Action (CTA) card component.
* It displays an image alongside a title, description, and an email subscription form.
* Designed to be theme-aware and fully customizable through props.
*/
export function CTACard({
title,
description,
imageSrc,
imageAlt = 'Promotional image',
inputPlaceholder = 'name@email.com',
buttonText = 'Subscribe',
onSubmit,
className,
}: CTACardProps) {
const [email, setEmail] = useState('');
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
onSubmit(email);
setEmail('');
};
return (
<Card
className={cn(
'w-full max-w-4xl overflow-hidden p-3 border rounded-3xl',
className
)}
>
{/*
DECISION: A 16-column grid is used on desktop to allow for fine-grained,
asymmetrical layouts (e.g., the 7/9 split used here) that a standard
12-column grid might not accommodate as cleanly.
*/}
<div className='grid grid-cols-1 md:grid-cols-16 gap-3'>
<div className='relative h-64 md:h-auto rounded-2xl md:col-span-7'>
<Image
src={imageSrc}
alt={imageAlt}
fill
className='object-cover rounded-2xl'
/>
</div>
<div className='flex flex-col w-full justify-start items-start rounded-2xl border md:col-span-9 p-6 md:p-8 gap-8 md:gap-16 lg:gap-20 bg-secondary'>
<CardHeader className='w-full p-0'>
<CardTitle className='text-2xl md:text-3xl font-bold'>
{title}
</CardTitle>
</CardHeader>
<CardContent className='flex w-full flex-col gap-2 p-0'>
<Label className='text-muted-foreground'>{description}</Label>
<form
onSubmit={handleSubmit}
className='mt-2 flex w-full flex-col sm:flex-row sm:space-x-2 space-y-2 sm:space-y-0'
>
<Input
type='email'
placeholder={inputPlaceholder}
value={email}
onChange={(e) => setEmail(e.target.value)}
className='flex-1 text-base'
aria-label='Email for newsletter'
required
/>
<Button type='submit'>{buttonText}</Button>
</form>
</CardContent>
</div>
</div>
</Card>
);
}Usage
Import the component and provide the required props to render the CTA card.
import { CTACard } from '@/components/ui/cta-card';
export default function MyPage() {
const handleSubscription = (email: string) => {
// Replace with your actual subscription logic
alert(`Subscribing with: ${email}`);
};
return (
<div className='w-full p-4 flex flex-col items-center gap-8 bg-background'>
{/* Example: Customized Usage */}
<CTACard
title='Unlock Exclusive Content'
description='Become a premium member to access our full library of resources and tools.'
imageSrc='https://images.unsplash.com/photo-1611974789855-9c2a0a7236a3'
imageAlt='Abstract financial graph'
inputPlaceholder='your-best@email.com'
buttonText='Get Access Now'
onSubmit={handleSubscription}
className='border-primary'
/>
</div>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | Required | The main headline or title for the CTA. |
description | string | Required | A short description supporting the title. |
imageSrc | string | Required | The source URL for the image. |
imageAlt | string | 'Promotional image' | Alt text for the image, for accessibility. |
inputPlaceholder | string | 'name@email.com' | Placeholder text for the email input field. |
buttonText | string | 'Subscribe' | Text displayed on the submission button. |
onSubmit | (email: string) => void | Required | Callback function executed when the form is submitted. |
className | string | — | Optional additional class names to apply to the root card element for custom styling. |