ID: 0x

|

DATE:

,

localStorage: Armazenando dados com JavaScript

AUTHOR:

|

READ_TIME: ~5 MIN

Ao criar uma preferência de tema, um rascunho curto ou o estado de um filtro, você nem sempre precisa enviar dados ao servidor. Nesses casos, o localStorage no JavaScript oferece uma API simples para manter pequenas informações no navegador.

No entanto, essa simplicidade vem com limites. O localStorage armazena apenas strings, executa operações de forma síncrona e não serve para guardar informações sensíveis. Neste guia, vamos entender a API, corrigir alguns erros comuns e montar uma pequena lista de tarefas.

O que é localStorage no JavaScript?

O localStorage faz parte da Web Storage API. Ele disponibiliza um armazenamento de pares de chave e valor para cada origem da aplicação.

Uma origem combina protocolo, host e porta. Portanto, estas duas URLs não compartilham o mesmo armazenamento:

  • http://example.com
  • https://example.com

Por outro lado, páginas em caminhos diferentes compartilham o mesmo localStorage quando pertencem à mesma origem.

Os dados permanecem disponíveis depois que a página ou o navegador fecha. Porém, isso não significa armazenamento permanente. O usuário pode apagar os dados, o navegador pode restringir o acesso e sessões privadas costumam removê-los ao final.

Além disso, o localStorage não é um cookie. Seus valores não são enviados automaticamente em requisições HTTP.

Como salvar e recuperar dados no localStorage

A interface Storage oferece cinco métodos principais:

  • setItem() adiciona ou atualiza um valor.
  • getItem() recupera um valor.
  • removeItem() remove uma chave.
  • clear() remove todas as chaves da origem.
  • key() retorna o nome de uma chave pela posição.

Para salvar uma preferência de tema:

localStorage.setItem("app:theme", "dark");Code language: JavaScript (javascript)

Em seguida, podemos recuperar o valor:

const theme = localStorage.getItem("app:theme");

if (theme !== null) {
  console.log(`Tema salvo: ${theme}`);
}Code language: JavaScript (javascript)

Quando a chave não existe, getItem() retorna null. Por isso, uma comparação explícita evita confundir uma chave ausente com uma string vazia.

Para remover apenas essa preferência:

localStorage.removeItem("app:theme");Code language: CSS (css)

Também existe o método clear():

localStorage.clear();Code language: CSS (css)

Use clear() com cuidado. Ele apaga todas as chaves do localStorage compartilhado pela origem, inclusive dados criados por outras partes da aplicação. Na maioria dos casos, removeItem() é uma escolha mais segura.

Podemos listar as chaves com length e key():

for (let index = 0; index < localStorage.length; index += 1) {
  const key = localStorage.key(index);
  console.log(key);
}Code language: JavaScript (javascript)

No entanto, a especificação não garante a ordem dessas chaves. Portanto, não use a posição como parte da lógica da aplicação.

Como visualizar localStorage no DevTools

No Chrome e em outros navegadores baseados em Chromium, abra o DevTools e acesse:

Application > Storage > Local Storage

Em seguida, selecione a origem da página. O painel permite inspecionar, editar e remover chaves durante o desenvolvimento.

No Firefox, os mesmos dados ficam no painel Storage. Os nomes e a posição das ferramentas podem mudar entre versões do navegador.

Como armazenar objetos com JSON

O localStorage aceita apenas strings. Para salvar arrays ou objetos simples, podemos convertê-los com JSON.stringify().

const userPreferences = {
  theme: "dark",
  compactLayout: true,
};

try {
  localStorage.setItem(
    "app:preferences",
    JSON.stringify(userPreferences),
  );
} catch (error) {
  console.error("Não foi possível salvar as preferências.", error);
}Code language: JavaScript (javascript)

Na leitura, fazemos o caminho inverso com JSON.parse():

function loadPreferences() {
  try {
    const savedValue = localStorage.getItem("app:preferences");

    if (savedValue === null) {
      return {};
    }

    return JSON.parse(savedValue);
  } catch (error) {
    console.warn("As preferências salvas são inválidas.", error);
    return {};
  }
}

const preferences = loadPreferences();Code language: JavaScript (javascript)

O try...catch é importante porque o conteúdo pode estar corrompido. Além disso, o navegador pode bloquear o armazenamento ou lançar QuotaExceededError quando a origem atinge o limite.

JSON também não preserva todos os tipos do JavaScript. Datas viram strings, funções são descartadas e valores como BigInt precisam de tratamento próprio.

Exemplo prático: lista de tarefas com localStorage

Vamos montar uma lista pequena para praticar leitura, gravação, atualização e remoção. O exemplo usa textContent para que o texto informado pelo usuário nunca seja interpretado como HTML.

Crie o arquivo index.html:

<!doctype html>
<html lang="pt-BR">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Lista de tarefas com localStorage</title>
    <script src="app.js" defer></script>
  </head>
  <body>
    <h1>Lista de tarefas</h1>

    <form id="task-form">
      <label for="task-description">Nova tarefa</label>
      <input
        id="task-description"
        name="description"
        maxlength="120"
        required
      >
      <button type="submit">Salvar</button>
    </form>

    <ul id="task-list"></ul>

    <button id="clear-completed" type="button">
      Limpar tarefas concluídas
    </button>

    <p id="status" role="status"></p>
  </body>
</html>Code language: HTML, XML (xml)

Agora adicione o arquivo app.js:

const STORAGE_KEY = "todo-list:v1";

const form = document.querySelector("#task-form");
const input = document.querySelector("#task-description");
const list = document.querySelector("#task-list");
const clearCompletedButton = document.querySelector("#clear-completed");
const status = document.querySelector("#status");

let tasks = loadTasks();

function loadTasks() {
  try {
    const savedValue = localStorage.getItem(STORAGE_KEY);

    if (savedValue === null) {
      return [];
    }

    const parsedValue = JSON.parse(savedValue);

    if (!Array.isArray(parsedValue)) {
      return [];
    }

    return parsedValue.filter((task) => {
      return (
        task &&
        typeof task.id === "string" &&
        typeof task.description === "string" &&
        typeof task.done === "boolean"
      );
    });
  } catch (error) {
    console.warn("Não foi possível ler as tarefas.", error);
    return [];
  }
}

function saveTasks() {
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
    status.textContent = "";
  } catch (error) {
    status.textContent =
      "Não foi possível salvar. Verifique o espaço e as permissões do navegador.";
    console.error(error);
  }
}

function renderTasks() {
  list.replaceChildren();

  for (const task of tasks) {
    const item = document.createElement("li");
    const description = document.createElement("span");
    const toggleButton = document.createElement("button");
    const removeButton = document.createElement("button");

    description.textContent = task.done
      ? `${task.description} (concluída)`
      : task.description;

    toggleButton.type = "button";
    toggleButton.dataset.action = "toggle";
    toggleButton.dataset.id = task.id;
    toggleButton.textContent = task.done ? "Reabrir" : "Concluir";

    removeButton.type = "button";
    removeButton.dataset.action = "remove";
    removeButton.dataset.id = task.id;
    removeButton.textContent = "Excluir";

    item.append(description, toggleButton, removeButton);
    list.appendChild(item);
  }
}

form.addEventListener("submit", (event) => {
  event.preventDefault();

  const description = input.value.trim();

  if (!description) {
    return;
  }

  tasks.push({
    id: `${Date.now()}`,
    description,
    done: false,
    createdAt: new Date().toISOString(),
  });

  saveTasks();
  renderTasks();
  form.reset();
  input.focus();
});

list.addEventListener("click", (event) => {
  const button = event.target.closest("button[data-action]");

  if (!button) {
    return;
  }

  const { action, id } = button.dataset;

  if (action === "toggle") {
    tasks = tasks.map((task) => {
      return task.id === id
        ? { ...task, done: !task.done }
        : task;
    });
  }

  if (action === "remove") {
    tasks = tasks.filter((task) => task.id !== id);
  }

  saveTasks();
  renderTasks();
});

clearCompletedButton.addEventListener("click", () => {
  tasks = tasks.filter((task) => !task.done);
  saveTasks();
  renderTasks();
});

renderTasks();Code language: JavaScript (javascript)

Esse exemplo resolve quatro problemas comuns. Primeiro, o estado done usa um boolean, não as strings "true" e "false". Segundo, filter() remove itens sem alterar o array durante um loop. Terceiro, textContent evita interpretar a descrição como HTML. Finalmente, a leitura e a gravação tratam possíveis erros.

Como sincronizar alterações entre abas

A página pode observar mudanças feitas por outra aba da mesma origem com o evento storage.

Adicione este trecho ao exemplo:

window.addEventListener("storage", (event) => {
  if (event.key !== STORAGE_KEY) {
    return;
  }

  tasks = loadTasks();
  renderTasks();
});Code language: JavaScript (javascript)

O evento não dispara na aba responsável pela alteração. Ele serve para avisar outras páginas que compartilham a mesma área de armazenamento.

Essa comunicação também não sincroniza dados entre browsers, dispositivos ou perfis. Para isso, a aplicação precisa de uma API e uma fonte de dados no servidor.

Limites e performance do localStorage

A Web Storage API reserva até 5 MiB de localStorage por origem. O sessionStorage possui outros 5 MiB. Quando a aplicação ultrapassa a quota, setItem() lança QuotaExceededError.

Esses valores são adequados para pequenas preferências e estados simples. No entanto, não são indicados para arquivos, imagens ou grandes respostas de API.

Outro ponto importante é a execução síncrona. Cada leitura e gravação bloqueia a execução do JavaScript até terminar. Dessa forma, valores grandes ou operações frequentes podem prejudicar a resposta da interface.

Para grandes conjuntos de dados, use IndexedDB. Para requisições e respostas HTTP, use a Cache API.

Segurança e privacidade

Qualquer script executado na mesma origem pode acessar o localStorage. Isso inclui scripts próprios, bibliotecas de terceiros e código injetado por uma vulnerabilidade XSS.

Por esse motivo, nunca armazene:

  • senhas;
  • tokens de sessão;
  • credenciais;
  • chaves privadas;
  • informações pessoais sensíveis.

O localStorage não possui o atributo HttpOnly disponível em cookies. Portanto, um cookie seguro, configurado pelo servidor, continua sendo a opção adequada para uma sessão de autenticação.

Além disso, trate dados lidos do armazenamento como entrada não confiável. Um usuário, uma extensão ou um script comprometido pode alterar o conteúdo.

Também ofereça uma forma clara de remover preferências locais. Caso os dados tenham finalidade de rastreamento ou personalização, avalie os requisitos de consentimento e privacidade aplicáveis ao projeto.

localStorage funciona em Service Workers?

Não. O localStorage pertence à interface Window, executa de modo síncrono e não está disponível dentro de Service Workers.

Em uma PWA, a página ainda pode usar localStorage para uma preferência pequena. Porém, o Service Worker precisa usar outras APIs:

  • Cache API para armazenar requisições e respostas;
  • IndexedDB para dados estruturados;
  • postMessage() para trocar mensagens com as páginas controladas.

Se você estiver começando nesse tema, leia também a introdução a aplicações web progressivas.

localStorage ou IndexedDB?

A escolha depende do volume e da forma dos dados.

RequisitolocalStorageIndexedDB
Modelo de dadosStrings com chave e valorObjetos estruturados
ExecuçãoSíncronaAssíncrona
VolumePequeno, até 5 MiBMaior, conforme a política do navegador
Consultas e índicesNãoSim
Web Workers e Service WorkersNãoSim
ComplexidadeBaixaMaior

Use o localStorage no JavaScript para preferências de interface, filtros e pequenos rascunhos sem dados sensíveis.

Escolha IndexedDB quando o projeto precisar de objetos, arquivos, índices, transações ou operações que não bloqueiem a interface.

Nenhuma das duas APIs oferece confidencialidade por padrão. IndexedDB não substitui criptografia, autenticação ou armazenamento seguro no servidor.

E quando usar sessionStorage?

O sessionStorage também armazena strings, mas seus dados pertencem à sessão da página e à aba atual. Em geral, o navegador remove esse conteúdo quando a aba fecha.

Portanto, ele funciona bem para estados temporários que não precisam permanecer entre visitas. Ainda assim, as mesmas recomendações de segurança continuam válidas.

Quando usar localStorage?

O localStorage funciona bem para:

  • tema claro ou escuro;
  • idioma e preferências de interface;
  • estado de filtros;
  • aviso já dispensado;
  • rascunhos pequenos sem informações sensíveis.

Evite usar a API como banco de dados principal, cache de imagens ou fonte de verdade de um carrinho de compras. Para esses cenários, combine APIs mais adequadas com persistência no servidor.

Em resumo, o localStorage no JavaScript continua útil e amplamente suportado. Use-o para dados pequenos, simples e não sensíveis. Além disso, trate erros, evite gravações frequentes e escolha IndexedDB ou Cache API quando o projeto crescer.

Referências


ENCODING: UTF-8

|

CHMOD: 644

// RELATED_ENTRIES

NEXT_READS

> cat ./comments.log

LOADING_ENTRIES…

4 respostas a “localStorage: Armazenando dados com JavaScript”

  1. porque que no começo das funções whatsYourName é sayMyName e colocado document.body.innerHTML = ‘ ‘? é muinto obrigado já me deu uma ajuda

    1. Fellyph Cintra

      Olá Wesley, boa pergunta. No caso essa atribuição é para simplesmente para limpar o conteúdo anterior salvo no body. Mas isso só por conta da função appendChild, ele não substitui o conteúdo anterior só adicionar conteúdo ao final do texto.

  2. izaias de oliveira elias

    Bom dia.
    Eu estou criando um site de notas com alerta de tempo de tarefa e estou tendouitos problemas com pouco conhecimento, vc poderia me ajudar?.
    Ótimo artigo.

  3. Salvou aqui mano, ajudou muito – Agradeço d+


> write ./comments.log –append

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *