DevQuestPROEstudos de Código
Snippet Vault

Códigos & Helpers de Produção

Custom hooks, validadores, formatadores e utilitários prontos para copiar e colar no seu projeto real.

Abrir Playground
8 Snippets Prontos
Hookstypescript

useDebounce Hook

Atrasa a atualização de um estado até que o usuário pare de digitar pelo intervalo definido. Ideal para campos de busca com autocompletion.

useDebounce Hook.tsTypeScript
import { useState, useEffect } from "react";

export function useDebounce<T>(value: T, delayMs: number = 300): T {
  const [debouncedValue, setDebouncedValue] = useState<T>(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delayMs);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delayMs]);

  return debouncedValue;
}
#React#Custom Hook#Performance#Input
Hookstypescript

useLocalStorage com Sync Entre Abas

Hook seguro para persistir estado no localStorage com parsing de JSON automático e listener para sincronizar quando outra aba atualizar o storage.

useLocalStorage com Sync Entre Abas.tsTypeScript
import { useState, useEffect, useCallback } from "react";

export function useLocalStorage<T>(key: string, initialValue: T): [T, (val: T | ((prev: T) => T)) => void] {
  const readValue = useCallback((): T => {
    if (typeof window === "undefined") return initialValue;
    try {
      const item = window.localStorage.getItem(key);
      return item ? (JSON.parse(item) as T) : initialValue;
    } catch (error) {
      console.warn(`Erro ao ler localStorage chave "${key}":`, error);
      return initialValue;
    }
  }, [key, initialValue]);

  const [storedValue, setStoredValue] = useState<T>(readValue);

  const setValue = useCallback(
    (value: T | ((prev: T) => T)) => {
      if (typeof window === "undefined") return;
      try {
        const newValue = value instanceof Function ? value(storedValue) : value;
        window.localStorage.setItem(key, JSON.stringify(newValue));
        setStoredValue(newValue);
        window.dispatchEvent(new StorageEvent("storage", { key, newValue: JSON.stringify(newValue) }));
      } catch (error) {
        console.warn(`Erro ao salvar localStorage chave "${key}":`, error);
      }
    },
    [key, storedValue]
  );

  useEffect(() => {
    const handleStorageChange = (e: StorageEvent) => {
      if (e.key === key && e.newValue) {
        setStoredValue(JSON.parse(e.newValue) as T);
      }
    };
    window.addEventListener("storage", handleStorageChange);
    return () => window.removeEventListener("storage", handleStorageChange);
  }, [key]);

  return [storedValue, setValue];
}
#React#Storage#State Management#Browser
Validationstypescript

Validador e Formatador de CPF

Algoritmo oficial com checagem dos 2 dígitos verificadores e função de máscara para formatação (000.000.000-00).

Validador e Formatador de CPF.tsTypeScript
export function isValidCPF(cpf: string): boolean {
  const clean = cpf.replace(/\D/g, "");

  if (clean.length !== 11) return false;
  // Elimina CPFs invalidos com todos os numeros iguais (111.111.111-11, etc)
  if (/^(\d)\1{10}$/.test(clean)) return false;

  let sum = 0;
  for (let i = 0; i < 9; i++) {
    sum += parseInt(clean.charAt(i), 10) * (10 - i);
  }
  let rev = 11 - (sum % 11);
  if (rev === 10 || rev === 11) rev = 0;
  if (rev !== parseInt(clean.charAt(9), 10)) return false;

  sum = 0;
  for (let i = 0; i < 10; i++) {
    sum += parseInt(clean.charAt(i), 10) * (11 - i);
  }
  rev = 11 - (sum % 11);
  if (rev === 10 || rev === 11) rev = 0;
  if (rev !== parseInt(clean.charAt(10), 10)) return false;

  return true;
}

export function maskCPF(cpf: string): string {
  const clean = cpf.replace(/\D/g, "").slice(0, 11);
  return clean
    .replace(/^(\d{3})(\d)/, "$1.$2")
    .replace(/^(\d{3})\.(\d{3})(\d)/, "$1.$2.$3")
    .replace(/\.(\d{3})(\d)/, ".$1-$2");
}
#Brasil#Regex#Formulários#Algoritmos
Utilstypescript

Formatador de Moeda BRL e Percentual

Formatação performática utilizando a API nativa Intl.NumberFormat sem dependências externas.

Formatador de Moeda BRL e Percentual.tsTypeScript
const brlCurrencyFormatter = new Intl.NumberFormat("pt-BR", {
  style: "currency",
  currency: "BRL",
  minimumFractionDigits: 2,
  maximumFractionDigits: 2,
});

export function formatBRL(amountInCentsOrNumber: number, isCents = false): string {
  const val = isCents ? amountInCentsOrNumber / 100 : amountInCentsOrNumber;
  return brlCurrencyFormatter.format(val);
}

const percentFormatter = new Intl.NumberFormat("pt-BR", {
  style: "percent",
  minimumFractionDigits: 1,
  maximumFractionDigits: 2,
});

export function formatPercent(rate: number): string {
  // rate: 0.15 => "15,0%"
  return percentFormatter.format(rate);
}
#Intl#E-commerce#Finanças#BRL
Utilstypescript

Gerador de Slugs Amigáveis (PT-BR)

Converte títulos em URLs limpas e otimizadas para SEO, tratando acentos (á, ç, õ), símbolos especiais e múltiplos hifens.

Gerador de Slugs Amigáveis (PT-BR).tsTypeScript
export function slugify(text: string): string {
  return text
    .toString()
    .normalize("NFD") // Decompõe caracteres acentuados
    .replace(/[\u0300-\u036f]/g, "") // Remove marcas diacríticas
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9\s-]/g, "") // Remove caracteres especiais
    .replace(/\s+/g, "-") // Troca espaços por hifens
    .replace(/-+/g, "-"); // Remove múltiplos hifens consecutivos
}
#SEO#Strings#Blog#URLs
Utilstypescript

Tempo Relativo ("há 5 minutos") com Intl

Calcula e exibe quanto tempo passou desde uma data ('há 2 horas', 'há 3 dias', 'ontem') sem bibliotecas pesadas como momentjs.

Tempo Relativo ("há 5 minutos") com Intl.tsTypeScript
export function formatRelativeTime(date: Date | string | number): string {
  const rtf = new Intl.RelativeTimeFormat("pt-BR", { numeric: "auto" });
  const timeMs = typeof date === "number" ? date : new Date(date).getTime();
  const diffSeconds = Math.round((timeMs - Date.now()) / 1000);

  const cutoffs = [
    { unit: "year", seconds: 31536000 },
    { unit: "month", seconds: 2592000 },
    { unit: "day", seconds: 86400 },
    { unit: "hour", seconds: 3600 },
    { unit: "minute", seconds: 60 },
    { unit: "second", seconds: 1 },
  ] as const;

  for (const { unit, seconds } of cutoffs) {
    if (Math.abs(diffSeconds) >= seconds || unit === "second") {
      const value = Math.round(diffSeconds / seconds);
      return rtf.format(value, unit);
    }
  }

  return "agora mesmo";
}
#Intl#Datas#UI#Feeds
Databasetypescript

Setup de Conexão Neon Postgres + Drizzle ORM

Configuração singleton pronta para uso no Next.js App Router e Server Actions com pool HTTP serverless.

Setup de Conexão Neon Postgres + Drizzle ORM.tsTypeScript
import { neon } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-http";
import * as schema from "./schema";

if (!process.env.DATABASE_URL) {
  throw new Error("DATABASE_URL is not set in environment variables");
}

// Cria cliente SQL serverless com suporte a scale-to-zero
const sql = neon(process.env.DATABASE_URL);

// Exporta instância do Drizzle tipada com o schema
export const db = drizzle(sql, { schema });
#Neon#Postgres#Drizzle#Backend
Performancetypescript

Rate Limiter em Memória para Rotas Next.js

Proteja endpoints de API contra spam e ataques de força bruta com um algoritmo simples de Sliding Window / Token Bucket em memória.

Rate Limiter em Memória para Rotas Next.js.tsTypeScript
interface RateLimitTracker {
  count: number;
  resetAt: number;
}

const rateLimitMap = new Map<string, RateLimitTracker>();

export function checkRateLimit(
  identifier: string,
  limit: number = 60,
  windowMs: number = 60000
): { allowed: boolean; remaining: number; resetInMs: number } {
  const now = Date.now();
  const entry = rateLimitMap.get(identifier);

  if (!entry || now > entry.resetAt) {
    rateLimitMap.set(identifier, {
      count: 1,
      resetAt: now + windowMs,
    });
    return { allowed: true, remaining: limit - 1, resetInMs: windowMs };
  }

  if (entry.count >= limit) {
    return { allowed: false, remaining: 0, resetInMs: entry.resetAt - now };
  }

  entry.count += 1;
  return { allowed: true, remaining: limit - entry.count, resetInMs: entry.resetAt - now };
}
#Segurança#API#Next.js#Backend