FAQ Component
The FAQ component displays frequently asked questions in an interactive accordion format with search functionality and custom SVG illustration.
Overview
This section:
- Displays 6 frequently asked questions
- Features accordion-style expand/collapse interaction
- Includes search functionality to filter questions
- Shows custom SVG illustration
- Handles “no results” state
- Single-question open at a time (optional multi-open)
Features
- Search Functionality: Real-time filtering of questions and answers
- Accordion Interaction: Click to expand/collapse answers
- Clear Search: X button to clear search query
- SVG Illustration: Custom decorative graphic
- No Results Handling: Friendly message when search yields no results
- State Management: Tracks active question and search query
- Inline SVG: Custom question mark illustration
Props
This component accepts no props.
Usage
import FAQ from './components/FAQ';
function App() {
return (
<>
{/* Other sections */}
<FAQ />
{/* More sections */}
</>
);
}
FAQ Data Structure
The component includes 6 FAQs:
const faqs = [
{
question: "¿Qué hace una agencia digital y en qué se diferencia de una agencia de marketing?",
answer: "Una agencia digital combina estrategia, diseño y tecnología para crear activos digitales (webs, apps, automatizaciones), mientras que muchas agencias de marketing se enfocan solo en campañas y redes. Nosotros construimos sistemas completos."
},
{
question: "¿Qué incluye un proyecto de branding y comunicación?",
answer: "Incluye el posicionamiento, mensaje, lineamientos visuales, arquitectura de comunicación y un plan base de contenidos para asegurar que tu marca sea coherente en todos los canales."
},
{
question: "¿Cuánto demora tener una web lista y optimizada para convertir?",
answer: "Depende del alcance, pero un sitio web estructurado o landing page suele tomar entre 4 a 8 semanas, incluyendo diseño, desarrollo y carga de contenidos."
},
{
question: "¿Qué automatizaciones se pueden implementar en un negocio digital?",
answer: "Desde respuestas automáticas y gestión de formularios, hasta integración de CRMs, facturación y flujos de nutrición de leads."
},
{
question: "¿Trabajan con proyectos en etapa temprana o solo con empresas?",
answer: "Trabajamos con ambos. Tenemos packs iniciales para startups que necesitan validar y ordenar, y soluciones avanzadas para empresas que buscan escalar."
},
{
question: "¿Cómo se mide el impacto del trabajo?",
answer: "Definimos KPIs claros al inicio: tráfico, conversiones, leads cualificados y ahorro de tiempo operativo son métricas comunes que seguimos."
}
];
Code Implementation
import React, { useState } from 'react';
const FAQ = () => {
const faqs = [
// ... FAQ data as shown above
];
const [activeIndex, setActiveIndex] = useState(null);
const [searchQuery, setSearchQuery] = useState('');
const toggleFAQ = (index) => {
setActiveIndex(activeIndex === index ? null : index);
};
const filteredFaqs = faqs.filter(faq =>
faq.question.toLowerCase().includes(searchQuery.toLowerCase()) ||
faq.answer.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<section id="faq" className="faq-section section-divider-dot">
<div className="container">
<h2 className="section-title">Preguntas Frecuentes</h2>
<div className="faq-content-wrapper">
<div className="faq-list">
<div className="faq-search-container">
<input
type="text"
placeholder="Buscar pregunta..."
className="faq-search-input"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
{searchQuery && (
<button
className="search-clear"
onClick={() => setSearchQuery('')}
aria-label="Limpiar búsqueda"
>
<svg>{/* X icon */}</svg>
</button>
)}
<span className="search-icon">
<svg>{/* Search icon */}</svg>
</span>
</div>
<div className="faq-items-container">
{filteredFaqs.length > 0 ? (
filteredFaqs.map((faq, index) => (
<div key={index} className={`faq-item-ref ${activeIndex === index ? 'active' : ''}`} onClick={() => toggleFAQ(index)}>
<div className="faq-question-ref">
<h3>{faq.question}</h3>
<span className={`faq-icon-ref ${activeIndex === index ? 'active' : ''}`}>
<svg>{/* Chevron down icon */}</svg>
</span>
</div>
<div className="faq-answer-ref">
<p>{faq.answer}</p>
</div>
</div>
))
) : (
<p className="faq-no-results">No se encontraron preguntas que coincidan con tu búsqueda.</p>
)}
</div>
</div>
<div className="faq-image">
{/* Custom SVG illustration */}
</div>
</div>
</div>
</section>
);
};
export default FAQ;
The 6 FAQ Questions
1. What does a digital agency do and how does it differ from a marketing agency?
Answer: A digital agency combines strategy, design, and technology to create digital assets (websites, apps, automations), while many marketing agencies focus only on campaigns and social media. We build complete systems.
2. What’s included in a branding and communication project?
Answer: Includes positioning, messaging, visual guidelines, communication architecture, and a base content plan to ensure your brand is coherent across all channels.
3. How long does it take to have a website ready and optimized for conversion?
Answer: Depends on scope, but a structured website or landing page typically takes 4 to 8 weeks, including design, development, and content loading.
4. What automations can be implemented in a digital business?
Answer: From automatic responses and form management, to CRM integration, invoicing, and lead nurturing flows.
5. Do you work with early-stage projects or only with companies?
Answer: We work with both. We have initial packs for startups that need to validate and organize, and advanced solutions for companies looking to scale.
6. How is the impact of the work measured?
Answer: We define clear KPIs at the start: traffic, conversions, qualified leads, and operational time savings are common metrics we track.
Search Functionality
Real-Time Filtering
Search filters both questions and answers:
const filteredFaqs = faqs.filter(faq =>
faq.question.toLowerCase().includes(searchQuery.toLowerCase()) ||
faq.answer.toLowerCase().includes(searchQuery.toLowerCase())
);
Conditionally shown when search has content:
{searchQuery && (
<button
className="search-clear"
onClick={() => setSearchQuery('')}
aria-label="Limpiar búsqueda"
>
{/* X icon */}
</button>
)}
No Results State
Friendly message when no matches found:
{filteredFaqs.length > 0 ? (
// Show filtered FAQs
) : (
<p className="faq-no-results">
No se encontraron preguntas que coincidan con tu búsqueda.
</p>
)}
The search is case-insensitive and searches both questions and answers for maximum discoverability.
Accordion Interaction
Toggle Function
Toggles between open and closed states:
const toggleFAQ = (index) => {
setActiveIndex(activeIndex === index ? null : index);
};
Logic:
- If clicked FAQ is already open (
activeIndex === index), close it (null)
- Otherwise, open the clicked FAQ (set
activeIndex to index)
Active State Styling
Both the FAQ item and icon receive active classes:
className={`faq-item-ref ${activeIndex === index ? 'active' : ''}`}
className={`faq-icon-ref ${activeIndex === index ? 'active' : ''}`}
SVG Illustration
Custom SVG graphic featuring:
- Question mark symbol with gradient
- Decorative circles
- Chat bubble illustrations
- Sparkle elements
SVG Structure
<svg width="100%" height="100%" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet">
{/* Background decorative circles */}
<circle cx="80" cy="80" r="40" fill="#667eea" opacity="0.1" />
<circle cx="320" cy="100" r="30" fill="#764ba2" opacity="0.1" />
<circle cx="350" cy="320" r="50" fill="#667eea" opacity="0.08" />
{/* Gradient definition */}
<defs>
<linearGradient id="questionGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#667eea" />
<stop offset="100%" stopColor="#764ba2" />
</linearGradient>
</defs>
{/* Main question mark */}
<path d="M 200 80 C 240 80 270 110 270 150 C 270 180 250 200 230 210 L 230 240"
stroke="url(#questionGradient)" strokeWidth="24" strokeLinecap="round" fill="none" />
<circle cx="230" cy="280" r="15" fill="url(#questionGradient)" />
{/* Chat bubbles and sparkles */}
</svg>
The SVG is fully responsive using preserveAspectRatio="xMidYMid meet" and percentage-based dimensions.
Styling Classes
.faq-section - Main section container
.section-divider-dot - Dotted divider style
.faq-content-wrapper - Two-column layout wrapper
.faq-list - FAQ list column
.faq-search-container - Search input wrapper
.faq-search-input - Search input field
.search-clear - Clear search button
.search-icon - Search icon
.faq-items-container - FAQ items wrapper
.faq-item-ref - Individual FAQ item
.faq-item-ref.active - Expanded FAQ item
.faq-question-ref - Question header
.faq-icon-ref - Chevron icon
.faq-icon-ref.active - Rotated chevron (when open)
.faq-answer-ref - Answer content (collapsed/expanded)
.faq-no-results - No results message
.faq-image - Illustration column
Layout Structure
┌──────────────────────────────────────────┐
│ Preguntas Frecuentes │
├───────────────────────┬───────────────────┤
│ [Search Input...] │ [SVG Illustration] │
│ │ │
│ ▶ Question 1... │ │
│ ▼ Question 2... │ │
│ Answer text... │ │
│ ▶ Question 3... │ │
│ ▶ Question 4... │ │
│ ▶ Question 5... │ │
│ ▶ Question 6... │ │
└───────────────────────┴───────────────────┘
State Management
Two state variables:
const [activeIndex, setActiveIndex] = useState(null); // Currently open FAQ (or null)
const [searchQuery, setSearchQuery] = useState(''); // Search input value
Responsive Behavior
- Desktop: Two-column layout (FAQs | Illustration)
- Tablet: May stack illustration below
- Mobile: Single column, illustration hidden or reduced
Accessibility Features
aria-label on clear button: “Limpiar búsqueda”
- Semantic HTML structure
- Keyboard-accessible buttons and inputs
Consider adding:
- ARIA expanded/collapsed states on accordion items
- Keyboard navigation (Arrow keys to move between questions)
- Focus management when opening/closing
SVG Icons Used
Search Icon (Magnifying Glass)
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
Clear Icon (X)
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
Chevron Down
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
Dependencies
- React hooks:
useState
- No external libraries required
- Inline SVG (no image files)
- Filtering happens on every keystroke (consider debouncing for large FAQ lists)
- SVG is inline (reduces HTTP requests but increases HTML size)
- Single active item prevents rendering all answers simultaneously
For very large FAQ lists (50+), consider implementing debounced search or virtual scrolling.