Map of India

This component centers around a fully interactive SVG-based India map that responds dynamically to user hover interactions. Each state is individually detectable, allowing precise region targeting and real-time visual feedback. When a user hovers over a state, it automatically rises in the SVG layer stack for emphasis while triggering a smoothly positioned tooltip that follows the cursor. The tooltip displays the active state name with animated styling, creating a clear and engaging geographic exploration experience. The system ensures accurate positioning, seamless transitions, and responsive interaction behavior across the entire map. By combining SVG manipulation, cursor tracking, and motion-based UI feedback, this implementation transforms a static map into an intuitive and interactive geographic interface optimized for modern web applications.

Implementation

Usage Guide

Integrate the component into your project by importing the vector asset and wrapping it in our motion container.

Fully Responsive
Dark Mode Ready
Download SVG Component Code

Parent Component. SVG component is wrapped into this. Just Copy-PASTE-USE

.tsx
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import IndiaMap from "./IndiaMap";

export default function MapPositionIndia() {
  const [tooltip, setTooltip] = useState<{
    name: string;
    x: number;
    y: number;
  } | null>(null);
  const [hover, setHover] = useState(false);

  const handleMouseMove = (e: React.MouseEvent<SVGPathElement>) => {
    const target = e.currentTarget;
    const name = target.dataset.name;

    if (!name) return;

    target.parentNode?.appendChild(target);

    setTooltip({
      name,
      x: e.clientX,
      y: e.clientY,
    });

    setHover(true);
  };

  const handleMouseLeave = () => {
    setTooltip(null);
    setHover(false);
  };

  return (
    <div className="relative w-full min-h-screen bg-white dark:bg-[#050505] overflow-hidden flex flex-col justify-center items-center transition-colors duration-500">
      {/* TOOLTIP */}
      <AnimatePresence>
        {tooltip && (
          <motion.div
            initial={{ opacity: 0, scale: 0.9, y: 20 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.95, y: 10 }}
            transition={{ type: "spring", stiffness: 150, damping: 20 }}
            className="fixed z-50 pointer-events-none"
            style={{
              left: tooltip.x,
              top: tooltip.y,
              transform: "translate(-50%, -50%)",
            }}
          >
            <motion.div
              initial={{ height: 0 }}
              animate={{ height: 140 }}
              className="absolute -top-[160px] left-0 -translate-x-1/2 w-px bg-gradient-to-b from-black dark:from-white via-black/10 to-transparent opacity-30"
            />

            <div className="absolute z-30 -top-[240px] -left-36 w-72">
              <div className="relative px-6 py-6 bg-white/90 dark:bg-black/90 backdrop-blur-xl border border-black/10 dark:border-white/10 shadow-2xl overflow-hidden">
               
                <div className="absolute top-0 left-0 w-2 h-2 border-t border-l border-black dark:border-white opacity-20" />
                <div className="absolute bottom-0 right-0 w-2 h-2 border-b border-r border-black dark:border-white opacity-20" />

                <div className="flex justify-between items-center mb-4">
                  <span className="font-mono text-[9px] uppercase tracking-[0.3em] opacity-40">
                    Geo.Registry
                  </span>
                  <div className="h-1 w-1 rounded-full bg-black dark:bg-white animate-pulse" />
                </div>

                <h2 className="text-3xl font-extralight tracking-tighter text-black dark:text-white uppercase">
                  {tooltip.name}
                </h2>

                <div className="mt-6 flex justify-between items-end border-t border-black/5 dark:border-white/5 pt-4">
                  <div className="space-y-1">
                    <p className="font-mono text-[8px] opacity-30">X_POS</p>
                    <p className="font-mono text-[10px] tabular-nums">
                      {tooltip.x.toFixed(1)}
                    </p>
                  </div>
                  <div className="space-y-1 text-right">
                    <p className="font-mono text-[8px] opacity-30">Y_POS</p>
                    <p className="font-mono text-[10px] tabular-nums">
                      {tooltip.y.toFixed(1)}
                    </p>
                  </div>
                </div>
              </div>
            </div>
          </motion.div>
        )}
      </AnimatePresence>

      {/* 2. THE MAP CONTAINER */}
      <motion.div
        className="w-full h-full flex justify-center items-center"
        animate={{
          rotateX: hover ? 2 : 0,
          rotateY: hover ? -2 : 0,
        }}
        transition={{ type: "spring", stiffness: 50, damping: 20 }}
      >
        <div className="w-full max-w-5xl px-12 my-32 ml-32 transition-all duration-700">
          <IndiaMap
            onMouseMove={handleMouseMove}
            onMouseLeave={handleMouseLeave}
            hover={hover}
            setHover={setHover}
            toolTipName={tooltip ? tooltip.name : ""}
          />
        </div>
      </motion.div>
      
      <div
        className="absolute inset-0 pointer-events-none opacity-[0.03] dark:opacity-[0.07]"
        style={{
          backgroundImage: `radial-gradient(circle, currentColor 1px, transparent 1px)`,
          backgroundSize: "40px 40px",
        }}
      />
    </div>
  );
}