Skip to main content

Outline

<> Step Code

Click on the link below to view the code for this step on GitHub.

This guide explains how to use free Figma resources and Vercel's v0 AI assistant to speed up your design and development process.

Introducing v0 from Vercel

v0 is an AI coding assistant by Vercel that helps developers quickly create React components with styling. It's useful for:

  • Rapid prototyping
  • Generating styled components
  • Learning new React patterns

Using Figma and v0 Together

  1. Start with a free Figma template
  2. Use v0 to turn Figma designs into code

Note

The feature for directly generating files from Figma designs in v0 is a premium feature. Only users with a paid subscription have this ability. Free users can still benefit by describing Figma designs to v0.

Example v0 Prompt

When using v0, provide context about your project. For example:

Create a React component based on the Figma design, considering:
- The project uses Optimizely SaaS CMS
- Components should be reusable blocks in components/blocks/
- Include separate Header and Footer components

v0 will generate a React component with styling that you can then customize for your project.

By combining Figma resources and v0, you can quickly create professional-looking components that fit your project's needs.

Generated Components with v0

To add components from shadcn/ui run the following commands:

npx shadcn@latest add button
npx shadcn@latest add input
npx shadcn@latest add textarea
npx shadcn@latest add card 
npx shadcn@latest add avatar
// components\block\contact-block.tsx
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"

interface ContactBlockProps {
  title: string
  description: string
}

export default function ContactBlock({ title, description }: ContactBlockProps) {
  return (
    <section className="container mx-auto px-4 py-16">
      <Card className="max-w-xl mx-auto">
        <CardHeader>
          <CardTitle>{title}</CardTitle>
          <p className="text-muted-foreground">{description}</p>
        </CardHeader>
        <CardContent>
          <form className="space-y-6">
            <Input placeholder="Name" />
            <Input placeholder="Email" type="email" />
            <Textarea placeholder="Message" />
            <Button className="w-full">Send</Button>
          </form>
        </CardContent>
      </Card>
    </section>
  )
}

// components\block\hero-block.tsx
interface HeroBlockProps {
  title: string
  subtitle?: string
  showDecoration?: boolean
  decorationColorsPrimary?: string
  decorationColorsSecondary?: string
}

export default function HeroBlock({
  title,
  subtitle,
  showDecoration = true,
  decorationColorsPrimary = "#009379",
  decorationColorsSecondary = "#ffd285"
}: HeroBlockProps) {
  return (
    <section className="container mx-auto px-4 pt-20 pb-16 relative">
      <h1 className="text-4xl md:text-6xl font-bold max-w-xl mb-4">{title}</h1>
      {subtitle && <p className="text-xl text-muted-foreground max-w-xl mb-8">{subtitle}</p>}
      {showDecoration && (
        <div className="absolute right-20 top-10">
          <div className="relative w-48 h-48">
            <div
              className="absolute right-0 w-32 h-32 rounded-full"
              style={{ backgroundColor: decorationColorsPrimary }}
            />
            <div
              className="absolute bottom-0 left-0 w-40 h-40 rounded-full"
              style={{ backgroundColor: decorationColorsSecondary }}
            />
          </div>
        </div>
      )}
    </section>
  )
}

//components\block\logos-block.tsx
import Image from "next/image"

interface Logo {
  src: string
  alt: string
}

interface LogosBlockProps {
  logos: Logo[]
}

export default function LogosBlock({ logos }: LogosBlockProps) {
  return (
    <section className="container mx-auto px-4 py-16">
      <div className="flex justify-center gap-12 flex-wrap">
        {logos.map((logo, index) => (
          <div key={index} className="flex items-center">
            <Image src={logo.src || "/placeholder.svg"} alt={logo.alt} width={100} height={40} />
          </div>
        ))}
      </div>
    </section>
  )
}
//components\block\portfolio-grid-block.tsx
import Image from "next/image"
import { Card, CardContent } from "@/components/ui/card"
import Link from "next/link"

interface PortfolioItem {
  title: string
  description: string
  imageUrl: string
  link: string
}

interface PortfolioGridBlockProps {
  title: string
  items: PortfolioItem[]
}

export default function PortfolioGridBlock({ title, items }: PortfolioGridBlockProps) {
  return (
    <section className="container mx-auto px-4 py-16">
      <h2 className="text-3xl font-bold mb-12">{title}</h2>
      <div className="grid md:grid-cols-3 gap-6">
        {items.map((item, index) => (
          <Card key={index}>
            <CardContent className="p-0">
              <Image
                src={item.imageUrl || "/placeholder.svg"}
                alt={item.title}
                width={400}
                height={300}
                className="w-full h-48 object-cover"
              />
              <div className="p-4">
                <Link href={item.link ?? ''}>
                  <h3 className="font-semibold mb-2">{item.title}</h3>
                </Link>
                <p className="text-sm text-muted-foreground">{item.description}</p>
              </div>
            </CardContent>
          </Card>
        ))}
      </div>
    </section>
  )
}
//components\block\services-block.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import type React from "react" // Import React
import Image from 'next/image'

interface Service {
  title: string
  description: string
  icon?: string
}

interface ServicesBlockProps {
  services: Service[]
}

export default function ServicesBlock({ services }: ServicesBlockProps) {
  return (
    <section className="container mx-auto px-4 py-16">
      <div className="grid md:grid-cols-3 gap-8">
        {services.map((service, index) => (
          <Card key={index}>
            <CardHeader>
              {service?.icon && <div className="mb-4"><Image src={service.icon} alt={service.title} width={50} height={50} /></div>}
              <CardTitle>{service.title}</CardTitle>
            </CardHeader>
            <CardContent>
              <p className="text-muted-foreground">{service.description}</p>
            </CardContent>
          </Card>
        ))}
      </div>
    </section>
  )
}
//components\block\testimonials-block.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"

interface Testimonial {
  name: string
  position: string
  content: string
  avatarSrc?: string
}

interface TestimonialsBlockProps {
  title: string
  testimonials: Testimonial[]
}

export default function TestimonialsBlock({ title, testimonials }: TestimonialsBlockProps) {
  return (
    <section className="container mx-auto px-4 py-16">
      <h2 className="text-3xl font-bold mb-12">{title}</h2>
      <div className="grid md:grid-cols-3 gap-8">
        {testimonials.map((testimonial, index) => (
          <Card key={index}>
            <CardHeader>
              <div className="flex items-center gap-4">
                <Avatar>
                  <AvatarImage src={testimonial.avatarSrc} alt={testimonial.name} />
                  <AvatarFallback>{testimonial.name.charAt(0)}</AvatarFallback>
                </Avatar>
                <div>
                  <CardTitle className="text-sm font-medium">{testimonial.name}</CardTitle>
                  <p className="text-sm text-muted-foreground">{testimonial.position}</p>
                </div>
              </div>
            </CardHeader>
            <CardContent>
              <p className="text-muted-foreground">{testimonial.content}</p>
            </CardContent>
          </Card>
        ))}
      </div>
    </section>
  )
}