CategoriesAllHeroNavigationFooterCall-to-ActionsFeature SectionsTestimonialsPricingContactGalleryStatsFAQServicesNewsletterAuthMiscellaneousButtons
All Components3D Cube InteractionBorder Logo GridCountdownFlip ButtonExpandable FAQsFlow ButtonGlow ButtonGrayscale CarouselInertia Zoom ParallaxLogo Cloud GridMobile Drawer NavigationPixel PreloaderProject GalleryRing ButtonShowcase CarouselSlide PreloaderSliding Stairs PreloaderStormtrooper FAQText Gradient OpacityThreads ButtonTilt HeadlineUltra Preloader
Code
app/page.tsx
1"use client"
2
3import ShowcaseCard from "@/components/showcase-card";
4import {
5 Carousel,
6 CarouselContent,
7 CarouselItem,
8 CarouselNext,
9 CarouselPrevious
10} from "@/components/ui/carousel";
11import Autoplay from "embla-carousel-autoplay";
12
13const ShowcasePage = () => {
14 return (
15 <section className=" w-full py-20 flex items-center justify-center bg-black h-screen">
16 <div className=" w-full relative group">
17 <div className="absolute top-0 bottom-0 left-0 w-[2%] md:w-[20%] z-[1] bg-gradient-to-r from-black to-transparent" />
18 <div className="absolute top-0 bottom-0 right-0 w-[2%] md:w-[20%] z-[1] bg-gradient-to-l from-black to-transparent" />
19
20 <Carousel
21 plugins={[
22 Autoplay({
23 delay: 5000,
24 }),
25 ]}
26 opts={{
27 align: "center",
28 loop: true,
29 }}
30 className="w-full"
31 >
32 <CarouselContent className="px-3 md:px-0">
33 <CarouselItem className=" basis-[90%] md:basis-1/4">
34 <ShowcaseCard color="#FB3F00" icon="/assets/showcase-icon-1.png" image="/assets/showcase-1.png" video="/videos/showcase-1.mp4" title="Lovi" link="www.lovi.com" />
35 </CarouselItem>
36 <CarouselItem className=" basis-[90%] md:basis-1/4">
37 <ShowcaseCard color="#1500FF" icon="/assets/showcase-icon-2.png" image="/assets/showcase-2.png" video="/videos/showcase-2.mp4" title="Lovi" link="www.lovi.com" />
38 </CarouselItem>
39 <CarouselItem className=" basis-[90%] md:basis-1/4">
40 <ShowcaseCard color="#85C25D" icon="/assets/showcase-icon-3.png" image="/assets/showcase-3.png" video="/videos/showcase-3.mp4" title="Lovi" link="www.lovi.com" />
41 </CarouselItem>
42 <CarouselItem className=" basis-[90%] md:basis-1/4">
43 <ShowcaseCard color="#FF8E1C" icon="/assets/showcase-icon-2.png" image="/assets/showcase-4.png" title="Lovi" link="www.lovi.com" />
44 </CarouselItem>
45 <CarouselItem className=" basis-[90%] md:basis-1/4">
46 <ShowcaseCard color="#4A4A2F" icon="/assets/showcase-icon-1.png" image="/assets/showcase-5.png" title="Lovi" link="www.lovi.com" />
47 </CarouselItem>
48 </CarouselContent>
49 <div className=" opacity-0 group-hover:opacity-100 group-hover:pointer-events-auto transition-opacity duration-200 ease-in pointer-events-none">
50 <CarouselPrevious />
51
52 <CarouselNext />
53 </div>
54 </Carousel>
55 </div>
56 </section>
57 );
58}
59
60export default ShowcasePage;Installation
Initialized a project and installed the necessary dependencies to use this component.
1. Initialize a new Next.js project:
npx create-next-app@latest my-app2. Install Shadcn UI
npx shadcn@latest init3. Install Shadcn Components
npx shadcn@latest add carousel button4. Install Embla Carousel Autoplay
npm install embla-carousel-autoplay --saveAdd Showcase Card Component
src/components/showcase-card.tsx
1import Image from "next/image";
2import Link from "next/link";
3
4const ShowcaseCard = ({
5 title,
6 image,
7 link,
8 video,
9 icon,
10 color
11}: {
12 title: string;
13 video?: string;
14 image: string;
15 link: string;
16 icon: string;
17 color: string;
18}) => {
19 return (
20 <div className=" w-full group/card">
21 {video ? (
22 <div style={{ backgroundColor: color }} className=" rounded-xl w-full p-2.5 md:p-4 relative">
23 <Link rel="" target="_blank" href={link}>
24 <div className="absolute hidden rounded-lg group-hover/card:block inset-0 z-20 hover:block cursor-pointer opacity-40" style={{ backgroundColor: color }} />
25 </Link>
26 <video
27 className="w-full h-auto aspect-square rounded-lg"
28 playsInline
29 autoPlay
30 loop
31 muted
32 poster={image}
33 >
34 <source src={video} type="video/mp4" />
35 Your browser does not support the video tag.
36 </video>
37 </div>
38 ) : (
39 <div className=" w-full relative h-auto aspect-square">
40 <Link rel="" target="_blank" href={link}>
41 <div className="absolute hidden rounded-lg group-hover/card:block inset-0 z-20 hover:block cursor-pointer opacity-40" style={{ backgroundColor: color }} />
42 </Link>
43 <Image
44 src={image}
45 alt={title}
46 fill
47 className="object-cover rounded-lg"
48 />
49 </div>
50 )}
51 <div className="flex items-center gap-4 mt-4">
52 <Image width={40} height={40} src={icon} className=" object-cover border rounded-full" alt={title} />
53 <div className="flex flex-col items-start text-white">
54 <p>{link}</p>
55 <p>{title}</p>
56 </div>
57 </div>
58 </div>
59 );
60}
61
62export default ShowcaseCard;Update Carousel Component
src/components/ui/carousel.tsx
1"use client"
2
3import useEmblaCarousel, {
4 type UseEmblaCarouselType,
5} from "embla-carousel-react"
6import { ChevronLeft, ChevronRight } from "lucide-react"
7import * as React from "react"
8
9import { Button } from "@/components/ui/button"
10import { cn } from "@/lib/utils"
11
12type CarouselApi = UseEmblaCarouselType[1]
13type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
14type CarouselOptions = UseCarouselParameters[0]
15type CarouselPlugin = UseCarouselParameters[1]
16
17type CarouselProps = {
18 opts?: CarouselOptions
19 plugins?: CarouselPlugin
20 orientation?: "horizontal" | "vertical"
21 setApi?: (api: CarouselApi) => void
22}
23
24type CarouselContextProps = {
25 carouselRef: ReturnType<typeof useEmblaCarousel>[0]
26 api: ReturnType<typeof useEmblaCarousel>[1]
27 scrollPrev: () => void
28 scrollNext: () => void
29 canScrollPrev: boolean
30 canScrollNext: boolean
31} & CarouselProps
32
33const CarouselContext = React.createContext<CarouselContextProps | null>(null)
34
35function useCarousel() {
36 const context = React.useContext(CarouselContext)
37
38 if (!context) {
39 throw new Error("useCarousel must be used within a <Carousel />")
40 }
41
42 return context
43}
44
45function Carousel({
46 orientation = "horizontal",
47 opts,
48 setApi,
49 plugins,
50 className,
51 children,
52 ...props
53}: React.ComponentProps<"div"> & CarouselProps) {
54 const [carouselRef, api] = useEmblaCarousel(
55 {
56 ...opts,
57 axis: orientation === "horizontal" ? "x" : "y",
58 },
59 plugins
60 )
61 const [canScrollPrev, setCanScrollPrev] = React.useState(false)
62 const [canScrollNext, setCanScrollNext] = React.useState(false)
63
64 const onSelect = React.useCallback((api: CarouselApi) => {
65 if (!api) return
66 setCanScrollPrev(api.canScrollPrev())
67 setCanScrollNext(api.canScrollNext())
68 }, [])
69
70 const scrollPrev = React.useCallback(() => {
71 api?.scrollPrev()
72 }, [api])
73
74 const scrollNext = React.useCallback(() => {
75 api?.scrollNext()
76 }, [api])
77
78 const handleKeyDown = React.useCallback(
79 (event: React.KeyboardEvent<HTMLDivElement>) => {
80 if (event.key === "ArrowLeft") {
81 event.preventDefault()
82 scrollPrev()
83 } else if (event.key === "ArrowRight") {
84 event.preventDefault()
85 scrollNext()
86 }
87 },
88 [scrollPrev, scrollNext]
89 )
90
91 React.useEffect(() => {
92 if (!api || !setApi) return
93 setApi(api)
94 }, [api, setApi])
95
96 React.useEffect(() => {
97 if (!api) return
98 onSelect(api)
99 api.on("reInit", onSelect)
100 api.on("select", onSelect)
101
102 return () => {
103 api?.off("select", onSelect)
104 }
105 }, [api, onSelect])
106
107 return (
108 <CarouselContext.Provider
109 value={{
110 carouselRef,
111 api: api,
112 opts,
113 orientation:
114 orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
115 scrollPrev,
116 scrollNext,
117 canScrollPrev,
118 canScrollNext,
119 }}
120 >
121 <div
122 onKeyDownCapture={handleKeyDown}
123 className={cn("relative", className)}
124 role="region"
125 aria-roledescription="carousel"
126 data-slot="carousel"
127 {...props}
128 >
129 {children}
130 </div>
131 </CarouselContext.Provider>
132 )
133}
134
135function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
136 const { carouselRef, orientation } = useCarousel()
137
138 return (
139 <div
140 ref={carouselRef}
141 className="overflow-hidden"
142 data-slot="carousel-content"
143 >
144 <div
145 className={cn(
146 "flex",
147 orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
148 className
149 )}
150 {...props}
151 />
152 </div>
153 )
154}
155
156function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
157 const { orientation } = useCarousel()
158
159 return (
160 <div
161 role="group"
162 aria-roledescription="slide"
163 data-slot="carousel-item"
164 className={cn(
165 "min-w-0 shrink-0 grow-0 basis-full",
166 orientation === "horizontal" ? "pl-4" : "pt-4",
167 className
168 )}
169 {...props}
170 />
171 )
172}
173
174function CarouselPrevious({
175 className,
176 variant = "outline",
177 size = "icon",
178 ...props
179}: React.ComponentProps<typeof Button>) {
180 const { orientation, scrollPrev, canScrollPrev } = useCarousel()
181
182 return (
183 <Button
184 data-slot="carousel-previous"
185 variant={variant}
186 size={size}
187 className={cn(
188 "absolute h-8 md:h-9 w-8 md:w-9 rounded-full hover:bg-white hover:text-black bg-black/50 backdrop-blur-md text-white border-none z-50",
189 orientation === "horizontal"
190 ? " left-12 md:left-[28rem] top-1/2 -translate-y-[100%]"
191 : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
192 className
193 )}
194 disabled={!canScrollPrev}
195 onClick={scrollPrev}
196 {...props}
197 >
198 <ChevronLeft className=" size-5" />
199 <span className="sr-only">Previous slide</span>
200 </Button>
201 )
202}
203
204function CarouselNext({
205 className,
206 variant = "outline",
207 size = "icon",
208 ...props
209}: React.ComponentProps<typeof Button>) {
210 const { orientation, scrollNext, canScrollNext } = useCarousel()
211
212 return (
213 <Button
214 data-slot="carousel-next"
215 variant={variant}
216 size={size}
217 className={cn(
218 "absolute h-8 md:h-9 w-8 md:w-9 rounded-full hover:bg-white bg-black/50 hover:text-black backdrop-blur-md text-white border-none z-50",
219 orientation === "horizontal"
220 ? " right-12 md:right-[28rem] top-1/2 -translate-y-[100%]"
221 : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
222 className
223 )}
224 disabled={!canScrollNext}
225 onClick={scrollNext}
226 {...props}
227 >
228 <ChevronRight className=" size-5" />
229 <span className="sr-only">Next slide</span>
230 </Button>
231 )
232}
233
234export {
235 Carousel,
236 CarouselContent,
237 CarouselItem, CarouselNext, CarouselPrevious, type CarouselApi
238}Add Button Component
src/components/ui/button.tsx
1import * as React from "react"
2import { Slot } from "@radix-ui/react-slot"
3import { cva, type VariantProps } from "class-variance-authority"
4
5import { cn } from "@/lib/utils"
6
7const buttonVariants = cva(
8 "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9 {
10 variants: {
11 variant: {
12 default:
13 "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
14 destructive:
15 "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
16 outline:
17 "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
18 secondary:
19 "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
20 ghost:
21 "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
22 link: "text-primary underline-offset-4 hover:underline",
23 },
24 size: {
25 default: "h-9 px-4 py-2 has-[>svg]:px-3",
26 sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
27 lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
28 icon: "size-9",
29 },
30 },
31 defaultVariants: {
32 variant: "default",
33 size: "default",
34 },
35 }
36)
37
38function Button({
39 className,
40 variant,
41 size,
42 asChild = false,
43 ...props
44}: React.ComponentProps<"button"> &
45 VariantProps<typeof buttonVariants> & {
46 asChild?: boolean
47 }) {
48 const Comp = asChild ? Slot : "button"
49
50 return (
51 <Comp
52 data-slot="button"
53 className={cn(buttonVariants({ variant, size, className }))}
54 {...props}
55 />
56 )
57}
58
59export { Button, buttonVariants }Add Global Styles
src/app/global.css
1@import "tailwindcss";
2@import "tw-animate-css";
3
4@custom-variant dark (&:is(.dark *));
5
6@theme inline {
7 --color-background: var(--background);
8 --color-foreground: var(--foreground);
9 --font-inter: var(--font-inter);
10 --color-sidebar-ring: var(--sidebar-ring);
11 --color-sidebar-border: var(--sidebar-border);
12 --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
13 --color-sidebar-accent: var(--sidebar-accent);
14 --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
15 --color-sidebar-primary: var(--sidebar-primary);
16 --color-sidebar-foreground: var(--sidebar-foreground);
17 --color-sidebar: var(--sidebar);
18 --color-chart-5: var(--chart-5);
19 --color-chart-4: var(--chart-4);
20 --color-chart-3: var(--chart-3);
21 --color-chart-2: var(--chart-2);
22 --color-chart-1: var(--chart-1);
23 --color-ring: var(--ring);
24 --color-input: var(--input);
25 --color-border: var(--border);
26 --color-destructive: var(--destructive);
27 --color-accent-foreground: var(--accent-foreground);
28 --color-accent: var(--accent);
29 --color-muted-foreground: var(--muted-foreground);
30 --color-muted: var(--muted);
31 --color-secondary-foreground: var(--secondary-foreground);
32 --color-secondary: var(--secondary);
33 --color-primary-foreground: var(--primary-foreground);
34 --color-primary: var(--primary);
35 --color-popover-foreground: var(--popover-foreground);
36 --color-popover: var(--popover);
37 --color-card-foreground: var(--card-foreground);
38 --color-card: var(--card);
39 --radius-sm: calc(var(--radius) - 4px);
40 --radius-md: calc(var(--radius) - 2px);
41 --radius-lg: var(--radius);
42 --radius-xl: calc(var(--radius) + 4px);
43}
44
45:root {
46 --radius: 0.625rem;
47 --background: oklch(1 0 0);
48 --foreground: oklch(0.145 0 0);
49 --card: oklch(1 0 0);
50 --card-foreground: oklch(0.145 0 0);
51 --popover: oklch(1 0 0);
52 --popover-foreground: oklch(0.145 0 0);
53 --primary: oklch(0.205 0 0);
54 --primary-foreground: oklch(0.985 0 0);
55 --secondary: oklch(0.97 0 0);
56 --secondary-foreground: oklch(0.205 0 0);
57 --muted: oklch(0.97 0 0);
58 --muted-foreground: oklch(0.556 0 0);
59 --accent: oklch(0.97 0 0);
60 --accent-foreground: oklch(0.205 0 0);
61 --destructive: oklch(0.577 0.245 27.325);
62 --border: oklch(0.922 0 0);
63 --input: oklch(0.922 0 0);
64 --ring: oklch(0.708 0 0);
65 --chart-1: oklch(0.646 0.222 41.116);
66 --chart-2: oklch(0.6 0.118 184.704);
67 --chart-3: oklch(0.398 0.07 227.392);
68 --chart-4: oklch(0.828 0.189 84.429);
69 --chart-5: oklch(0.769 0.188 70.08);
70 --sidebar: oklch(0.985 0 0);
71 --sidebar-foreground: oklch(0.145 0 0);
72 --sidebar-primary: oklch(0.205 0 0);
73 --sidebar-primary-foreground: oklch(0.985 0 0);
74 --sidebar-accent: oklch(0.97 0 0);
75 --sidebar-accent-foreground: oklch(0.205 0 0);
76 --sidebar-border: oklch(0.922 0 0);
77 --sidebar-ring: oklch(0.708 0 0);
78}
79
80.dark {
81 --background: oklch(0.145 0 0);
82 --foreground: oklch(0.985 0 0);
83 --card: oklch(0.205 0 0);
84 --card-foreground: oklch(0.985 0 0);
85 --popover: oklch(0.205 0 0);
86 --popover-foreground: oklch(0.985 0 0);
87 --primary: oklch(0.922 0 0);
88 --primary-foreground: oklch(0.205 0 0);
89 --secondary: oklch(0.269 0 0);
90 --secondary-foreground: oklch(0.985 0 0);
91 --muted: oklch(0.269 0 0);
92 --muted-foreground: oklch(0.708 0 0);
93 --accent: oklch(0.269 0 0);
94 --accent-foreground: oklch(0.985 0 0);
95 --destructive: oklch(0.704 0.191 22.216);
96 --border: oklch(1 0 0 / 10%);
97 --input: oklch(1 0 0 / 15%);
98 --ring: oklch(0.556 0 0);
99 --chart-1: oklch(0.488 0.243 264.376);
100 --chart-2: oklch(0.696 0.17 162.48);
101 --chart-3: oklch(0.769 0.188 70.08);
102 --chart-4: oklch(0.627 0.265 303.9);
103 --chart-5: oklch(0.645 0.246 16.439);
104 --sidebar: oklch(0.205 0 0);
105 --sidebar-foreground: oklch(0.985 0 0);
106 --sidebar-primary: oklch(0.488 0.243 264.376);
107 --sidebar-primary-foreground: oklch(0.985 0 0);
108 --sidebar-accent: oklch(0.269 0 0);
109 --sidebar-accent-foreground: oklch(0.985 0 0);
110 --sidebar-border: oklch(1 0 0 / 10%);
111 --sidebar-ring: oklch(0.556 0 0);
112}
113
114@layer base {
115 * {
116 @apply border-border outline-ring/50;
117 }
118 body {
119 @apply bg-background text-foreground;
120 }
121}