All-Access · 22 templates$1270.97$99/year — save 92%Get access
  • HomeHome
  • TemplatesTemplates23
  • ComponentsComponents78
  • PricingPricing
  • Need a Website?Need a Website?New
Unlock All AccessUnlock All Access

Our latest template collection

Ready-to-launch sites for real estate agents, service businesses, and local brands — built to rank, load fast, and convert.

Explore All TemplatesExplore All Templates

Orvaine - Luxury Equestrian & Horse Breeding Website Template

Orvaine is a premium website template for stud farms, sport horse breeders and equestrian estates: horse pages with pedigrees, events, films. Next.js, Astro, Sanity.

Download Template
Orvaine - Luxury Equestrian & Horse Breeding Website Template

Maressa - Luxury Yacht Charter & Sales Website Template

Maressa is a premium website template for yacht charter, brokerage and superyacht brands: 21 editable sections, full-screen films, Next.js and Astro versions. Sanity.

Download Template
Maressa - Luxury Yacht Charter & Sales Website Template

Krishi – Agriculture & Farm Website Template

Krishi is a premium website template for farms, agritech and food brands: 17 editable sections, cinematic GSAP motion and both Next.js and Astro versions. Built with Sanity.

Download Template
Krishi – Agriculture & Farm Website Template
ComponentsCarousel

Centered Loop Slider

View code
Full screen

A centred photo slider with peeking neighbours, large labels and arrows that loop seamlessly. React and Tailwind CSS, no carousel library.

CenteredLoopSlider.tsx
"use client";

// Centred photo slider: the active slide sits in the middle at 70% width with
// its neighbours peeking in, large labels over each photo, and arrows that loop.
// Pure Tailwind + React, no carousel library. Works with Tailwind v3 and v4.

import { useEffect, useState } from "react";

type Slide = { src: string; label?: string; alt: string };

type Props = {
  slides?: Slide[];
};

const img = (id: string) => `https://images.unsplash.com/photo-${id}?w=1600&q=80&auto=format&fit=crop`;

const DEFAULT_SLIDES: Slide[] = [
  { src: img("1582719478250-c89cae4dc85b"), label: "Bedroom", alt: "Suite bedroom" },
  { src: img("1560448204-e02f11c3d0e2"), label: "Living", alt: "Bright living room" },
  { src: img("1697124510322-27ef594f67fd"), label: "Lounge", alt: "Lounge with sea view" },
  { src: img("1502986591842-471865a47d0e"), label: "Cabin", alt: "Round porthole window" },
];

const Arrow = ({ flip = false }: { flip?: boolean }) => (
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.75} strokeLinecap="round" aria-hidden className={`h-5 w-5 ${flip ? "rotate-180" : ""}`}>
    <path d="M5 12h14M13 6l6 6-6 6" />
  </svg>
);

export default function CenteredLoopSlider({ slides = DEFAULT_SLIDES }: Props) {
  const count = slides.length;
  // The list is rendered three times; `current` moves freely and snaps back to the
  // middle copy (without animating) after each move, so looping never rewinds
  const loop = [...slides, ...slides, ...slides];
  const [current, setCurrent] = useState(count);
  const [animate, setAnimate] = useState(true);

  useEffect(() => {
    if (animate) return;
    const id = requestAnimationFrame(() => setAnimate(true));
    return () => cancelAnimationFrame(id);
  }, [animate]);

  // Clamped so very fast clicking can't run past the rendered copies
  const go = (step: number) => setCurrent((c) => Math.min(Math.max(c + step, 0), loop.length - 1));
  const onSettled = () => {
    if (current < count || current >= count * 2) {
      setAnimate(false);
      setCurrent(count + (((current - count) % count) + count) % count);
    }
  };

  return (
    <section className="overflow-hidden bg-white py-16 font-['Inter',ui-sans-serif,system-ui,sans-serif] antialiased" aria-roledescription="carousel">
      <div className="relative">
        <div
          onTransitionEnd={(e) => e.target === e.currentTarget && onSettled()}
          className={`flex ${animate ? "transition-transform duration-700 ease-[cubic-bezier(0.22,1,0.36,1)]" : ""}`}
          style={{ transform: `translateX(${15 - current * 70}%)` }}
        >
          {loop.map((s, i) => (
            <div key={i} aria-hidden={i !== current} className="relative w-[70%] shrink-0 px-1">
              <div className="overflow-hidden">
                <img src={s.src} alt={s.alt} loading="lazy" className={`h-[320px] w-full object-cover transition-opacity duration-700 md:h-[500px] ${i === current ? "opacity-100" : "opacity-60"}`} />
              </div>
              {s.label && <div className="absolute inset-0 flex items-center justify-center font-['Playfair_Display',Georgia,serif] text-[clamp(2.5rem,6vw,5rem)] font-medium text-white">{s.label}</div>}
            </div>
          ))}
        </div>

        <div className="pointer-events-none absolute inset-x-0 top-1/2 flex -translate-y-1/2 justify-between px-4 md:px-8">
          <button type="button" onClick={() => go(-1)} aria-label="Previous slide" className="pointer-events-auto grid h-14 w-14 place-items-center rounded-full bg-white text-[#49031E] shadow-lg transition-colors hover:bg-[#99023C] hover:text-white">
            <Arrow flip />
          </button>
          <button type="button" onClick={() => go(1)} aria-label="Next slide" className="pointer-events-auto grid h-14 w-14 place-items-center rounded-full bg-white text-[#49031E] shadow-lg transition-colors hover:bg-[#99023C] hover:text-white">
            <Arrow />
          </button>
        </div>
      </div>
    </section>
  );
}

How to use it

  1. Make sure your Next.js or React project has Tailwind CSS set up (v3 or v4 both work).
  2. Create components/CenteredLoopSlider.tsx and paste the code.
  3. Import it into a page and replace the sample text and images with your own.
Meridian - Luxury Resort & Hotel Website Template

From the full template

Meridian$49.99

Every page, Sanity CMS and SEO already set up. This section is one of 3 you can try free.

Get the full template Get the full template Live preview Live preview
Share
Category
CarouselGallery
Template
Meridian
Dependencies
None, just React
Works with
Next.js, React, Tailwind CSS v3 & v4
Added
Oct 29, 2026

More from Meridian

See the full template
Facilities Hover Reveal Grid
Feature

Facilities Hover Reveal Grid

Parallax Image CTA
CTA

Parallax Image CTA

More carousel

View all
Arched Portrait Carousel
Carousel

Arched Portrait Carousel

Curved 3D Carousel
Carousel

Curved 3D Carousel

Highlights Carousel
Carousel

Highlights Carousel

Ready for a faster site that's easier to edit?

Book a free 30-minute call. We'll review your current site and send a fixed-price plan. No pressure, no pitch.

Book Free Call Book Free Call No commitment required

Fast Next.js + Sanity websites, built for you or ready to launch with our templates.

Book a Free Call Book a Free Call
Services
  • Web Design
  • UI/UX Design
  • Framer Dev
  • Webflow Dev
  • Custom Dev
  • Sanity CMS
Resources
  • Templates
  • Pricing
  • License
  • Components
  • Case Studies
Company
  • About Us
  • Contact
  • Privacy Policy
© 2026 rayso.studio — All rights reservedDesigned & built in-house Remote-first agency
Let's TalkLet's Talk