Retour au catalogue

Contact Map Integrated

Section contact avec carte Google Maps integree en fond et overlay d'informations.

contactsimple Both Responsive a11y
minimalcorporaterestaurantbeautyplumbinguniversalstacked
Theme

Cómo crear una sección de contacto con mapa integrado en React

Una sección de contacto React con mapa integrado muestra un iframe de Google Maps dentro de un contenedor redondeado y, debajo del mapa, presenta dirección, email y teléfono en una barra de información fija. Framer Motion anima ambos bloques a medida que el usuario hace scroll.

  • Stack: React + Framer Motion + lucide-react + Tailwind v4, ~170 líneas, sin dependencias adicionales.
  • El iframe lleva loading='lazy' y referrerPolicy='no-referrer-when-downgrade' por rendimiento y privacidad.
  • Todos los colores provienen de propiedades CSS personalizadas (--color-background, --color-accent, etc.), sin valores codificados a mano.
  • Accesible: el iframe tiene un atributo title; el email y el teléfono son enlaces anchor reales (mailto:/tel:).
  • Responsive por defecto, el mapa pasa de h-80 a h-96 en el breakpoint lg.

Contact Map Integrated combina un mapa integrado a todo el ancho con una barra de información compacta que muestra dirección, email y teléfono en una sola fila fácil de leer. Los dos bloques entran con scroll mediante Framer Motion, el segundo bloque con un desfase de 100 ms respecto al primero. Es ideal para cualquier marca con local físico que necesita que los visitantes la encuentren rápido: restaurantes, estudios, clínicas.

Anatomía

La sección tiene dos bloques motion.div dentro de un contenedor max-w-6xl. El primero contiene el badge, el h2 y el subtítulo opcional. El segundo es un div redondeado con un borde de 1 px que envuelve dos hijos: el área del mapa (un iframe o un icono MapPin de fallback cuando no se proporciona ninguna URL) y la barra de información debajo, que usa flex-wrap para organizar los elementos de contacto y empuja el enlace CTA al extremo derecho con ml-auto.

Cómo funciona

Ambos bloques usan el mismo patrón de Framer Motion: initial={{ opacity: 0, y: 20 }}, whileInView={{ opacity: 1, y: 0 }}, viewport={{ once: true }}. La curva cubic-bezier [0.16, 1, 0.3, 1] da a cada bloque una subida rápida que desacelera de forma suave, más marcada que el easeOut por defecto y cercana a un spring sin necesidad de useSpring. El segundo bloque añade delay: 0.1 para crear un ligero desfase. El iframe se encarga de su propio renderizado del mapa; React solo gestiona el contenedor y la interfaz que lo rodea.

Cómo crearlo en React

  1. Crear la envoltura de la sección y el bloque de cabecera

    Envuelve todo en una etiqueta section con un token CSS para el fondo. Dentro de un contenedor max-w-6xl, coloca un motion.div que sube con el scroll para el badge, el h2 y el subtítulo. Este bloque se anima de forma independiente al mapa, lo que da tiempo a que la cabecera aparezca antes de que el mapa la siga.

    const ease: [number, number, number, number] = [0.16, 1, 0.3, 1];
    
    <motion.div
      initial={{ opacity: 0, y: 20 }}
      whileInView={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.6, ease }}
      viewport={{ once: true }}
    >
      {badge && <span className="uppercase tracking-widest text-xs">{badge}</span>}
      <h2>{title}</h2>
    </motion.div>
  2. Integrar el iframe del mapa con un fallback

    Renderiza el iframe cuando la prop mapEmbedUrl está presente; muestra un icono MapPin centrado en caso contrario. Pon loading='lazy' para diferir el iframe hasta que esté cerca del viewport. El atributo title del iframe es obligatorio para la accesibilidad, los lectores de pantalla lo anuncian como landmark.

    {mapEmbedUrl ? (
      <iframe
        src={mapEmbedUrl}
        className="w-full h-full"
        style={{ border: 0 }}
        allowFullScreen
        loading="lazy"
        referrerPolicy="no-referrer-when-downgrade"
        title="Carte"
      />
    ) : (
      <div className="flex items-center justify-center h-full">
        <MapPin size={40} />
      </div>
    )}
  3. Construir la barra de información de contacto

    Debajo del mapa, añade una fila flex-wrap con los tres elementos de contacto (dirección, email, teléfono) renderizados condicionalmente. Cada elemento combina un icono de lucide con su texto o enlace anchor. Empuja el enlace CTA de cómo llegar a la derecha con ml-auto. Los enlaces de teléfono usan tel: tras eliminar los espacios; los emails usan mailto:.

    <a href={`tel:${phone.replace(/\s/g, "")}`} className="text-sm hover:opacity-70">
      {phone}
    </a>
    <a href={ctaUrl} target="_blank" rel="noopener noreferrer" className="ml-auto flex items-center gap-2">
      {ctaLabel} <ExternalLink size={14} />
    </a>
  4. Desfasar el bloque del mapa 100 ms

    Envuelve el contenedor de mapa redondeado en un segundo motion.div con la misma config initial/whileInView, pero añade delay: 0.1 a la transición. Esto crea una revelación en dos tiempos, la cabecera se asienta primero, el mapa sigue, sin ninguna orquestación compleja.

    <motion.div
      initial={{ opacity: 0, y: 20 }}
      whileInView={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.6, ease, delay: 0.1 }}
      viewport={{ once: true }}
      className="rounded-xl overflow-hidden"
    >
      {/* map + info bar */}
    </motion.div>

Cuándo usarlo

Usa esta sección cuando la ubicación física es una señal de conversión clave: restaurantes, salones de belleza, clínicas, espacios de coworking, cualquier servicio que dependa del tránsito de personas. Colócala al final de la página, después de los testimonios o las FAQ, donde los visitantes que ya han decidido visitar van a confirmar la dirección. Omítela para servicios totalmente remotos, productos SaaS o páginas donde un formulario de contacto por sí solo es suficiente.

Usado por

  • Airbnb, Las páginas de anuncios combinan un mapa integrado con barras de información de estilo contacto que muestran los datos del anfitrión y la ubicación.
  • OpenTable, Las páginas de perfil de restaurante muestran un mapa interactivo junto a la dirección, el teléfono y el CTA de cómo llegar en un layout apilado.
  • Doctolib, Las páginas de profesionales muestran la ubicación de la consulta en un mapa junto a una franja de información con dirección y número de teléfono.
  • WeWork, Las páginas de ubicación integran mapas sobre una barra que resume los datos de contacto y el itinerario de cada espacio de coworking.

FAQ

¿Cómo obtengo una URL de integración de Google Maps?

Abre Google Maps, busca la ubicación, haz clic en Compartir, elige Insertar un mapa, copia el valor src del fragmento de iframe. Pégalo en la prop mapEmbedUrl. Ten en cuenta que la Maps Embed API es gratuita para este caso de uso, pero requiere una clave API de navegador para sitios de alto tráfico.

¿Puedo reemplazar Google Maps por OpenStreetMap?

Sí, cambia el src del iframe por una URL de integración de OpenStreetMap (p. ej. https://www.openstreetmap.org/export/embed.html?bbox=...). El componente solo renderiza lo que indica el src; ningún código específico de Google vive en la capa React.

¿El mapa afecta al rendimiento de la página?

El iframe usa loading='lazy' para que el navegador difiera la carga del mapa hasta que la sección esté cerca del viewport. Para páginas con un presupuesto de Core Web Vitals estricto, considera reemplazar el iframe por una imagen de mapa estática que abra Google Maps al hacer clic, esto evita por completo la carga de scripts de terceros.

¿Cómo oculto el mapa en móvil y muestro solo los datos de contacto?

Añade una clase de Tailwind como 'hidden sm:block' al div contenedor del iframe. La barra de información de abajo es independiente del layout y se muestra bien por sí sola en cualquier ancho de viewport.

"use client";

import { motion } from "framer-motion";
import { MapPin, Mail, Phone, ExternalLink } from "lucide-react";

interface ContactMapIntegratedProps {
  badge?: string;
  title?: string;
  subtitle?: string;
  mapEmbedUrl?: string;
  address?: string;
  email?: string;
  phone?: string;
  ctaLabel?: string;
  ctaUrl?: string;
}

const ease: [number, number, number, number] = [0.16, 1, 0.3, 1];

export default function ContactMapIntegrated({
  badge = "Nous trouver",
  title = "Venez nous rendre visite",

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Reseñas

Sección de contacto React con Google Maps integrado,