Source profileQuality 66/100

affaan-m/ECC/docs/es/skills/frontend-patterns/SKILL.md

frontend-patterns

Patrones de desarrollo frontend para React, Next.js, gestión de estado, optimización de rendimiento y buenas prácticas de UI.

Source repository stars
234,327
Declared platforms
0
Static risk flags
1
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Patrones modernos de frontend para React, Next.js e interfaces de usuario de alto rendimiento.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "docs/es/skills/frontend-patterns"
    Safe inspection promptEditorial

    Inspect the Agent Skill "frontend-patterns" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/es/skills/frontend-patterns/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Cuándo Activar

      Construir componentes React (composición, props, renderizado)

      Construir componentes React (composición, props, renderizado)Gestionar estado (useState, useReducer, Zustand, Context)Implementar obtención de datos (SWR, React Query, server components)
    2. 02

      Patrones de Componentes

      Review the “Patrones de Componentes” section in the pinned source before continuing.

      Review and apply the “Patrones de Componentes” source section.
    3. 03

      Composición sobre Herencia

      Review the “Composición sobre Herencia” section in the pinned source before continuing.

      Review and apply the “Composición sobre Herencia” source section.
    4. 04

      Compound Components

      Review the “Compound Components” section in the pinned source before continuing.

      Review and apply the “Compound Components” source section.
    5. 05

      Patrón Render Props

      Review the “Patrón Render Props” section in the pinned source before continuing.

      Review and apply the “Patrón Render Props” source section.

    Permission review

    Static risk signals and limitations

    Network access

    medium · line 109

    The documentation includes network, browsing, or remote request actions.

    fetch(url)

    Network access

    medium · line 195

    The documentation includes network, browsing, or remote request actions.

    () => fetch('/api/markets').then(r => r.json()),

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score66/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    docs/es/skills/frontend-patterns/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Patrones de Desarrollo Frontend

    Patrones modernos de frontend para React, Next.js e interfaces de usuario de alto rendimiento.

    Cuándo Activar

    • Construir componentes React (composición, props, renderizado)
    • Gestionar estado (useState, useReducer, Zustand, Context)
    • Implementar obtención de datos (SWR, React Query, server components)
    • Optimizar rendimiento (memoización, virtualización, code splitting)
    • Trabajar con formularios (validación, inputs controlados, esquemas Zod)
    • Manejar routing y navegación del lado del cliente
    • Construir patrones de UI accesibles y responsivos

    Patrones de Componentes

    Composición sobre Herencia

    // PASS: BIEN: Composición de componentes
    interface CardProps {
      children: React.ReactNode
      variant?: 'default' | 'outlined'
    }
    
    export function Card({ children, variant = 'default' }: CardProps) {
      return <div className={`card card-${variant}`}>{children}</div>
    }
    
    export function CardHeader({ children }: { children: React.ReactNode }) {
      return <div className="card-header">{children}</div>
    }
    
    export function CardBody({ children }: { children: React.ReactNode }) {
      return <div className="card-body">{children}</div>
    }
    
    // Uso
    <Card>
      <CardHeader>Title</CardHeader>
      <CardBody>Content</CardBody>
    </Card>
    

    Compound Components

    interface TabsContextValue {
      activeTab: string
      setActiveTab: (tab: string) => void
    }
    
    const TabsContext = createContext<TabsContextValue | undefined>(undefined)
    
    export function Tabs({ children, defaultTab }: {
      children: React.ReactNode
      defaultTab: string
    }) {
      const [activeTab, setActiveTab] = useState(defaultTab)
    
      return (
        <TabsContext.Provider value={{ activeTab, setActiveTab }}>
          {children}
        </TabsContext.Provider>
      )
    }
    
    export function TabList({ children }: { children: React.ReactNode }) {
      return <div className="tab-list">{children}</div>
    }
    
    export function Tab({ id, children }: { id: string, children: React.ReactNode }) {
      const context = useContext(TabsContext)
      if (!context) throw new Error('Tab must be used within Tabs')
    
      return (
        <button
          className={context.activeTab === id ? 'active' : ''}
          onClick={() => context.setActiveTab(id)}
        >
          {children}
        </button>
      )
    }
    
    // Uso
    <Tabs defaultTab="overview">
      <TabList>
        <Tab id="overview">Overview</Tab>
        <Tab id="details">Details</Tab>
      </TabList>
    </Tabs>
    

    Patrón Render Props

    interface DataLoaderProps<T> {
      url: string
      children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode
    }
    
    export function DataLoader<T>({ url, children }: DataLoaderProps<T>) {
      const [data, setData] = useState<T | null>(null)
      const [loading, setLoading] = useState(true)
      const [error, setError] = useState<Error | null>(null)
    
      useEffect(() => {
        fetch(url)
          .then(res => res.json())
          .then(setData)
          .catch(setError)
          .finally(() => setLoading(false))
      }, [url])
    
      return <>{children(data, loading, error)}</>
    }
    
    // Uso
    <DataLoader<Market[]> url="/api/markets">
      {(markets, loading, error) => {
        if (loading) return <Spinner />
        if (error) return <Error error={error} />
        return <MarketList markets={markets!} />
      }}
    </DataLoader>
    

    Patrones de Custom Hooks

    Hook de Gestión de Estado

    export function useToggle(initialValue = false): [boolean, () => void] {
      const [value, setValue] = useState(initialValue)
    
      const toggle = useCallback(() => {
        setValue(v => !v)
      }, [])
    
      return [value, toggle]
    }
    
    // Uso
    const [isOpen, toggleOpen] = useToggle()
    

    Hook de Obtención de Datos Asíncrona

    interface UseQueryOptions<T> {
      onSuccess?: (data: T) => void
      onError?: (error: Error) => void
      enabled?: boolean
    }
    
    export function useQuery<T>(
      key: string,
      fetcher: () => Promise<T>,
      options?: UseQueryOptions<T>
    ) {
      const [data, setData] = useState<T | null>(null)
      const [error, setError] = useState<Error | null>(null)
      const [loading, setLoading] = useState(false)
    
      const refetch = useCallback(async () => {
        setLoading(true)
        setError(null)
    
        try {
          const result = await fetcher()
          setData(result)
          options?.onSuccess?.(result)
        } catch (err) {
          const error = err as Error
          setError(error)
          options?.onError?.(error)
        } finally {
          setLoading(false)
        }
      }, [fetcher, options])
    
      useEffect(() => {
        if (options?.enabled !== false) {
          refetch()
        }
      }, [key, refetch, options?.enabled])
    
      return { data, error, loading, refetch }
    }
    
    // Uso
    const { data: markets, loading, error, refetch } = useQuery(
      'markets',
      () => fetch('/api/markets').then(r => r.json()),
      {
        onSuccess: data => console.log('Fetched', data.length, 'markets'),
        onError: err => console.error('Failed:', err)
      }
    )
    

    Hook de Debounce

    export function useDebounce<T>(value: T, delay: number): T {
      const [debouncedValue, setDebouncedValue] = useState<T>(value)
    
      useEffect(() => {
        const handler = setTimeout(() => {
          setDebouncedValue(value)
        }, delay)
    
        return () => clearTimeout(handler)
      }, [value, delay])
    
      return debouncedValue
    }
    
    // Uso
    const [searchQuery, setSearchQuery] = useState('')
    const debouncedQuery = useDebounce(searchQuery, 500)
    
    useEffect(() => {
      if (debouncedQuery) {
        performSearch(debouncedQuery)
      }
    }, [debouncedQuery])
    

    Patrones de Gestión de Estado

    Patrón Context + Reducer

    interface State {
      markets: Market[]
      selectedMarket: Market | null
      loading: boolean
    }
    
    type Action =
      | { type: 'SET_MARKETS'; payload: Market[] }
      | { type: 'SELECT_MARKET'; payload: Market }
      | { type: 'SET_LOADING'; payload: boolean }
    
    function reducer(state: State, action: Action): State {
      switch (action.type) {
        case 'SET_MARKETS':
          return { ...state, markets: action.payload }
        case 'SELECT_MARKET':
          return { ...state, selectedMarket: action.payload }
        case 'SET_LOADING':
          return { ...state, loading: action.payload }
        default:
          return state
      }
    }
    
    const MarketContext = createContext<{
      state: State
      dispatch: Dispatch<Action>
    } | undefined>(undefined)
    
    export function MarketProvider({ children }: { children: React.ReactNode }) {
      const [state, dispatch] = useReducer(reducer, {
        markets: [],
        selectedMarket: null,
        loading: false
      })
    
      return (
        <MarketContext.Provider value={{ state, dispatch }}>
          {children}
        </MarketContext.Provider>
      )
    }
    
    export function useMarkets() {
      const context = useContext(MarketContext)
      if (!context) throw new Error('useMarkets must be used within MarketProvider')
      return context
    }
    

    Optimización de Rendimiento

    Memoización

    // PASS: useMemo para cómputos costosos
    const sortedMarkets = useMemo(() => {
      return markets.sort((a, b) => b.volume - a.volume)
    }, [markets])
    
    // PASS: useCallback para funciones pasadas a hijos
    const handleSearch = useCallback((query: string) => {
      setSearchQuery(query)
    }, [])
    
    // PASS: React.memo para componentes puros
    export const MarketCard = React.memo<MarketCardProps>(({ market }) => {
      return (
        <div className="market-card">
          <h3>{market.name}</h3>
          <p>{market.description}</p>
        </div>
      )
    })
    

    Code Splitting y Carga Diferida

    import { lazy, Suspense } from 'react'
    
    // PASS: Carga diferida de componentes pesados
    const HeavyChart = lazy(() => import('./HeavyChart'))
    const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
    
    export function Dashboard() {
      return (
        <div>
          <Suspense fallback={<ChartSkeleton />}>
            <HeavyChart data={data} />
          </Suspense>
    
          <Suspense fallback={null}>
            <ThreeJsBackground />
          </Suspense>
        </div>
      )
    }
    

    Virtualización para Listas Largas

    import { useVirtualizer } from '@tanstack/react-virtual'
    
    export function VirtualMarketList({ markets }: { markets: Market[] }) {
      const parentRef = useRef<HTMLDivElement>(null)
    
      const virtualizer = useVirtualizer({
        count: markets.length,
        getScrollElement: () => parentRef.current,
        estimateSize: () => 100,  // Altura estimada de fila
        overscan: 5  // Elementos extra a renderizar
      })
    
      return (
        <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
          <div
            style={{
              height: `${virtualizer.getTotalSize()}px`,
              position: 'relative'
            }}
          >
            {virtualizer.getVirtualItems().map(virtualRow => (
              <div
                key={virtualRow.index}
                style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  width: '100%',
                  height: `${virtualRow.size}px`,
                  transform: `translateY(${virtualRow.start}px)`
                }}
              >
                <MarketCard market={markets[virtualRow.index]} />
              </div>
            ))}
          </div>
        </div>
      )
    }
    

    Patrones de Manejo de Formularios

    Formulario Controlado con Validación

    interface FormData {
      name: string
      description: string
      endDate: string
    }
    
    interface FormErrors {
      name?: string
      description?: string
      endDate?: string
    }
    
    export function CreateMarketForm() {
      const [formData, setFormData] = useState<FormData>({
        name: '',
        description: '',
        endDate: ''
      })
    
      const [errors, setErrors] = useState<FormErrors>({})
    
      const validate = (): boolean => {
        const newErrors: FormErrors = {}
    
        if (!formData.name.trim()) {
          newErrors.name = 'Name is required'
        } else if (formData.name.length > 200) {
          newErrors.name = 'Name must be under 200 characters'
        }
    
        if (!formData.description.trim()) {
          newErrors.description = 'Description is required'
        }
    
        if (!formData.endDate) {
          newErrors.endDate = 'End date is required'
        }
    
        setErrors(newErrors)
        return Object.keys(newErrors).length === 0
      }
    
      const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault()
    
        if (!validate()) return
    
        try {
          await createMarket(formData)
          // Manejo de éxito
        } catch (error) {
          // Manejo de error
        }
      }
    
      return (
        <form onSubmit={handleSubmit}>
          <input
            value={formData.name}
            onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}
            placeholder="Market name"
          />
          {errors.name && <span className="error">{errors.name}</span>}
    
          {/* Otros campos */}
    
          <button type="submit">Create Market</button>
        </form>
      )
    }
    

    Patrón Error Boundary

    interface ErrorBoundaryState {
      hasError: boolean
      error: Error | null
    }
    
    export class ErrorBoundary extends React.Component<
      { children: React.ReactNode },
      ErrorBoundaryState
    > {
      state: ErrorBoundaryState = {
        hasError: false,
        error: null
      }
    
      static getDerivedStateFromError(error: Error): ErrorBoundaryState {
        return { hasError: true, error }
      }
    
      componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
        console.error('Error boundary caught:', error, errorInfo)
      }
    
      render() {
        if (this.state.hasError) {
          return (
            <div className="error-fallback">
              <h2>Something went wrong</h2>
              <p>{this.state.error?.message}</p>
              <button onClick={() => this.setState({ hasError: false })}>
                Try again
              </button>
            </div>
          )
        }
    
        return this.props.children
      }
    }
    
    // Uso
    <ErrorBoundary>
      <App />
    </ErrorBoundary>
    

    Patrones de Animación

    Animaciones con Framer Motion

    import { motion, AnimatePresence } from 'framer-motion'
    
    // PASS: Animaciones de lista
    export function AnimatedMarketList({ markets }: { markets: Market[] }) {
      return (
        <AnimatePresence>
          {markets.map(market => (
            <motion.div
              key={market.id}
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: -20 }}
              transition={{ duration: 0.3 }}
            >
              <MarketCard market={market} />
            </motion.div>
          ))}
        </AnimatePresence>
      )
    }
    
    // PASS: Animaciones de modal
    export function Modal({ isOpen, onClose, children }: ModalProps) {
      return (
        <AnimatePresence>
          {isOpen && (
            <>
              <motion.div
                className="modal-overlay"
                initial={{ opacity: 0 }}
                animate={{ opacity: 1 }}
                exit={{ opacity: 0 }}
                onClick={onClose}
              />
              <motion.div
                className="modal-content"
                initial={{ opacity: 0, scale: 0.9, y: 20 }}
                animate={{ opacity: 1, scale: 1, y: 0 }}
                exit={{ opacity: 0, scale: 0.9, y: 20 }}
              >
                {children}
              </motion.div>
            </>
          )}
        </AnimatePresence>
      )
    }
    

    Patrones de Accesibilidad

    Navegación por Teclado

    export function Dropdown({ options, onSelect }: DropdownProps) {
      const [isOpen, setIsOpen] = useState(false)
      const [activeIndex, setActiveIndex] = useState(0)
    
      const handleKeyDown = (e: React.KeyboardEvent) => {
        switch (e.key) {
          case 'ArrowDown':
            e.preventDefault()
            setActiveIndex(i => Math.min(i + 1, options.length - 1))
            break
          case 'ArrowUp':
            e.preventDefault()
            setActiveIndex(i => Math.max(i - 1, 0))
            break
          case 'Enter':
            e.preventDefault()
            onSelect(options[activeIndex])
            setIsOpen(false)
            break
          case 'Escape':
            setIsOpen(false)
            break
        }
      }
    
      return (
        <div
          role="combobox"
          aria-expanded={isOpen}
          aria-haspopup="listbox"
          onKeyDown={handleKeyDown}
        >
          {/* Implementación del Dropdown */}
        </div>
      )
    }
    

    Gestión de Foco

    export function Modal({ isOpen, onClose, children }: ModalProps) {
      const modalRef = useRef<HTMLDivElement>(null)
      const previousFocusRef = useRef<HTMLElement | null>(null)
    
      useEffect(() => {
        if (isOpen) {
          // Guardar elemento actualmente enfocado
          previousFocusRef.current = document.activeElement as HTMLElement
    
          // Enfocar modal
          modalRef.current?.focus()
        } else {
          // Restaurar foco al cerrar
          previousFocusRef.current?.focus()
        }
      }, [isOpen])
    
      return isOpen ? (
        <div
          ref={modalRef}
          role="dialog"
          aria-modal="true"
          tabIndex={-1}
          onKeyDown={e => e.key === 'Escape' && onClose()}
        >
          {children}
        </div>
      ) : null
    }
    

    Recuerda: Los patrones modernos de frontend permiten interfaces de usuario mantenibles y de alto rendimiento. Elige los patrones que se ajusten a la complejidad de tu proyecto.

    Alternatives

    Compare before choosing