Operador ternário em JavaScript: if em uma linha
O ternário é expressão, não instrução — por isso cabe dentro de template literal e de JSX. Aninhamento, comparação com ?? e o SyntaxError do : sozinho.
O operador ternário escolhe entre dois valores conforme uma condição:
condicao ? valorSeVerdadeiro : valorSeFalso. É o único operador da linguagem
que recebe três operandos — daí o nome.
A diferença que importa não é o tamanho. O ternário é uma expressão: ele
devolve um valor. O if é uma instrução: ele executa um bloco e não devolve
nada. Por isso o ternário cabe dentro de um template literal, dentro de um
argumento de função e dentro do JSX — três lugares onde um if simplesmente
não entra.
Se você ainda não viu a versão longa da decisão, ela está em if else em JavaScript, a lição anterior.
Uma bifurcação que precisa devolver um valor
Imagine uma etiqueta de frete com um único espaço: se o pedido passou de cem reais, escreva “grátis”; caso contrário, escreva o preço. A decisão precisa terminar com um valor para ocupar aquele espaço. Esse é o cenário em que o ternário encaixa naturalmente.
Tecnicamente, o operador ternário é uma expressão condicional. Ele avalia a condição e devolve exatamente um dos dois operandos; por isso cabe numa variável, num argumento ou no JSX. No primeiro exemplo, cubra os dois valores com a mão, avalie a condição e escolha um. Depois confira se a saída mostra justamente o ramo previsto.
const total = 250;
const frete = total >= 199 ? 0 : 24.9;
console.log(frete);
console.log(total >= 199 ? 'Frete grátis' : 'Frete: R$ 24,90');Repare na segunda linha: o ternário foi direto para dentro do console.log,
sem variável intermediária. Ele é um valor, e valor vai onde valor vai.
Expressão contra instrução, lado a lado
Este par mostra por que o ternário existe. Com if, você precisa declarar a
variável antes, vazia, e preencher nos dois braços:
const total = 120;
let freteComIf;
if (total >= 199) {
freteComIf = 0;
} else {
freteComIf = 24.9;
}
const freteComTernario = total >= 199 ? 0 : 24.9;
console.log(freteComIf, freteComTernario);
console.log(typeof (total >= 199 ? 0 : 24.9));Duas coisas mudaram além do tamanho. freteComIf teve que ser let, porque
recebe valor depois — e uma variável que pode ser reatribuída é uma variável que
alguém pode reatribuir. Com o ternário, freteComTernario é const: nasce com
o valor final e ninguém mexe mais.
A última linha é a prova de que estamos falando de expressão: typeof só se
aplica a algo que tem tipo. typeof (if ...) não é código válido.
Dentro de um template literal
Este é o uso mais comum no dia a dia, e o que o if não consegue imitar:
const pedido = { cliente: 'Ana', itens: 3, total: 289.9 };
console.log(
`${pedido.cliente} tem ${pedido.itens} ${pedido.itens === 1 ? 'item' : 'itens'} ` +
`e paga ${pedido.total >= 199 ? 'frete grátis' : 'R$ 24,90 de frete'}`,
);
const vazio = { cliente: 'Bruno', itens: 1, total: 49.9 };
console.log(
`${vazio.cliente} tem ${vazio.itens} ${vazio.itens === 1 ? 'item' : 'itens'} ` +
`e paga ${vazio.total >= 199 ? 'frete grátis' : 'R$ 24,90 de frete'}`,
);Dentro de ${ } só cabe expressão. Pluralizar “item/itens” com if exigiria
montar a frase em pedaços, numa variável, antes da interpolação — três linhas
para resolver uma letra.
Dentro do JSX
Em React vale a mesma regra: { } no JSX aceita expressão, não instrução. Por
isso o ternário é onipresente em componente:
function ResumoDoPedido({ pedido }) {
return (
<div className="pedido">
<h2>Pedido #{pedido.id}</h2>
<span className={pedido.pago ? 'badge badge-ok' : 'badge badge-alerta'}>
{pedido.pago ? 'Pago' : 'Aguardando pagamento'}
</span>
{pedido.itens.length > 0 ? (
<ul>
{pedido.itens.map((item) => (
<li key={item.id}>{item.nome}</li>
))}
</ul>
) : (
<p>Carrinho vazio</p>
)}
</div>
);
}Três ternários, três funções diferentes: escolher uma classe CSS, escolher um texto e escolher entre dois blocos de marcação. Nos três casos o resultado é colocado em algum lugar — e é isso que exige expressão.
Aninhar: até onde a escada ainda lê
Ternário dentro de ternário é legítimo — o operador é associativo à direita, e o
segundo : fecha a segunda pergunta. Formatado como escada, funciona bem para
faixas:
function etiquetaDeFrete(total) {
return total >= 199
? 'grátis'
: total >= 99
? 'R$ 9,90'
: total >= 49
? 'R$ 14,90'
: 'R$ 24,90';
}
console.log(etiquetaDeFrete(250));
console.log(etiquetaDeFrete(120));
console.log(etiquetaDeFrete(60));
console.log(etiquetaDeFrete(30));Lê de cima para baixo como uma tabela de faixas, e cada linha é uma pergunta. O limite prático é este: enquanto todas as condições testarem a mesma variável, na mesma direção, a escada continua honesta.
Ela para de valer quando as perguntas passam a ser sobre coisas diferentes —
total, depois cliente.plano, depois endereco.uf. Nesse ponto a estrutura
já não é uma faixa, é uma tabela de regras, e tabela se escreve como dado:
const FAIXAS = [
{ minimo: 199, etiqueta: 'grátis' },
{ minimo: 99, etiqueta: 'R$ 9,90' },
{ minimo: 49, etiqueta: 'R$ 14,90' },
];
function etiquetaDeFrete(total) {
return FAIXAS.find((faixa) => total >= faixa.minimo)?.etiqueta ?? 'R$ 24,90';
}
console.log(etiquetaDeFrete(250));
console.log(etiquetaDeFrete(120));
console.log(etiquetaDeFrete(60));
console.log(etiquetaDeFrete(30));Mesma saída. A diferença é que acrescentar uma faixa agora é acrescentar uma linha no array, e o marketing consegue conferir a tabela sem saber ler código.
?? e &&: quando o ternário é ruído
Boa parte dos ternários que aparecem em revisão de código estão escolhendo entre “o valor” e “um padrão”. Para isso existem operadores mais curtos — e um deles está certo enquanto o outro está errado:
const cupomDoFormulario = '';
const apelidoDoCliente = null;
const quantidade = 0;
console.log(cupomDoFormulario ? cupomDoFormulario : 'SEMCUPOM');
console.log(cupomDoFormulario || 'SEMCUPOM');
console.log(cupomDoFormulario ?? 'SEMCUPOM');
console.log(apelidoDoCliente ?? 'cliente');
console.log(quantidade || 1);
console.log(quantidade ?? 1);cliente 1 0
A terceira linha da saída está vazia: ?? só substitui null e undefined, e
string vazia é um valor legítimo. Já quantidade || 1 transformou o zero em um —
o que numa loja significa vender uma unidade que o cliente não pediu.
Escolha assim: ?? para valor ausente, || só quando qualquer falsy deve
mesmo virar o padrão, e ternário quando a condição não é sobre ausência.
Quando existe apenas o caso verdadeiro, o && resolve — com uma pegadinha:
const pedido = { cliente: 'Ana', total: 289.9, pago: true };
const badge = pedido.pago && 'PAGO';
const alerta = !pedido.pago && 'AGUARDANDO PAGAMENTO';
console.log(badge);
console.log(alerta);
console.log(`<span>${pedido.pago ? 'PAGO' : 'AGUARDANDO'}</span>`);alerta virou o booleano false, não uma string vazia. Em JSX isso é inofensivo
(React não renderiza false), mas numa concatenação de string você imprime a
palavra “false” na tela. Se o resultado vai virar texto, use ternário com os dois
braços explícitos.
Erros comuns: SyntaxError: Unexpected token ':'
O erro clássico do ternário é perder um ? no meio de uma cadeia aninhada. Os
dois pontos ficam sem a pergunta correspondente:
const total = 120;
const etiqueta = total >= 199
? 'grátis'
: total >= 99
? 'R$ 9,90'
: total >= 49
: 'R$ 14,90';
console.log(etiqueta);SyntaxError: Unexpected token ‘:’ at compileSourceTextModule (node:internal/modules/esm/utils:318:16) at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:90:18) at #translate (node:internal/modules/esm/loader:435:20)
Node.js v24.16.0
O Node aponta a linha 8 — o : que sobrou —, mas o que falta está na linha 7:
total >= 49 ficou sem o ? depois. Como todo SyntaxError, ele acontece antes
de qualquer execução: o console.log da linha 10 não chegou a existir.
Ternário com efeito colateral: não
O último ponto é de disciplina. Como o ternário aceita qualquer expressão nos
braços, dá para chamar função e ignorar o retorno — usando um operador de valor
como se fosse um if:
const carrinho = { itens: 2 };
let log = [];
const registrar = (msg) => {
log.push(msg);
return msg;
};
carrinho.itens > 0 ? registrar('checkout liberado') : registrar('carrinho vazio');
console.log(log);
const rotulo = carrinho.itens > 0 ? 'checkout liberado' : 'carrinho vazio';
console.log(rotulo);A linha do meio funciona: ela registrou a mensagem certa. Mas o valor produzido
pelo ternário foi jogado fora, e quem lê o código precisa entrar nas duas funções
para descobrir que ali existe um efeito. Isso é um if disfarçado — e um if
disfarçado é pior que um if.
O critério é direto: se você usa o resultado, ternário; se você quer executar
algo, if.
Como decidir na hora
| situação | escreva |
|---|---|
| escolher entre dois valores para atribuir | ternário |
| escolher texto dentro de template literal ou JSX | ternário |
valor padrão para null/undefined |
?? |
| só existe o caso verdadeiro, em JSX | && |
| executar um bloco, com efeito colateral | if |
| três ou mais faixas da mesma variável | ternário em escada ou tabela de faixas |
| muitos valores fixos de uma variável | switch ou objeto |
A próxima lição da trilha de JavaScript é o switch,
que é o que sobra quando a pergunta deixa de ser “verdadeiro ou falso” e passa a
ser “qual dos oito valores”. O
guia completo de JavaScript mostra onde isso entra no
percurso.
Perguntas frequentes
Ternário é mais rápido que if?
Posso aninhar ternários?
Qual a diferença entre ternário e o operador ??
Por que o ternário aparece tanto em React?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- MDN — Operador condicional (ternário) — developer.mozilla.org
- MDN — Nullish coalescing operator (??) — developer.mozilla.org


