Props no React: passar dados de um componente para outro
Como declarar, receber e desestruturar props, definir valor default, passar função e por que alterar uma prop dentro do filho quebra na hora.
Imagine cadastrar mil livros numa loja virtual. Se cada cartão tiver título, autor e preço escritos diretamente no componente, você precisará copiar a mesma estrutura mil vezes. Uma correção no layout vira mil correções — e basta esquecer uma para a prateleira ficar inconsistente.
Props resolvem essa repetição: você cria um único cartão e entrega dados
diferentes a cada uso. Neste artigo, a livraria cresce do primeiro CardLivro
até uma Prateleira real, com preço, destaque e botão de compra.
As tags misturadas ao JavaScript usam JSX, sintaxe que descreve a interface como HTML, a marcação que organiza a página. Se for novo, revise JSX no React e volte sem pressa.
O que são props no React e para que servem?
Props são os valores que um componente React recebe de quem o utiliza. Elas permitem reaproveitar a mesma estrutura com textos, números, objetos e ações diferentes, sem duplicar o componente.
Componente é uma função que descreve parte da interface. Quem usa outro componente é o pai; o usado é o filho. O pai envia as props, e o filho as lê num objeto, valor que reúne informações em pares de nome e conteúdo.
Pense num molde de caixa usado para vários pedidos. A estrutura da caixa é a mesma; a etiqueta informa título, preço e destino. O componente é o molde, e as props são a etiqueta entregue por quem o usa. O limite da analogia é que a caixa é passiva: o componente executa JavaScript e produz uma descrição da tela.
Em <CardLivro titulo="Dom Casmurro" preco={34.9} />, titulo e preco chegam
como propriedades de props. Em vez de argumentos separados, o componente
recebe esse único objeto.
import { renderToStaticMarkup } from 'react-dom/server';
function CardLivro(props) {
return (
<article className="card-livro">
<h3>{props.titulo}</h3>
<p>{props.autor}</p>
<strong>R$ {props.preco.toFixed(2)}</strong>
</article>
);
}
console.log(renderToStaticMarkup(<CardLivro titulo="Dom Casmurro" autor="Machado de Assis" preco={34.9} />));
console.log(renderToStaticMarkup(<CardLivro titulo="Vidas Secas" autor="Graciliano Ramos" preco={42} />));
console.log(renderToStaticMarkup(<CardLivro titulo="Torto Arado" autor="Itamar Vieira Junior" preco={59.9} />));O import traz o renderizador do teste. CardLivro recebe props; o return
descreve o article; className liga regras visuais de CSS; e as chaves inserem
título, autor e preço. toFixed(2) mantém duas casas decimais. Cada log muda o
objeto de entrada, e a saída prova: três HTMLs, uma função, nenhuma cópia.
Experimente você mesmo: preveja o segundo cartão
Antes de reler a segunda saída, observe apenas o segundo uso de CardLivro.
Preveja título, autor e preço — incluindo as duas casas decimais. Depois confira
se a estrutura HTML permaneceu igual e somente os dados mudaram.
Esse objeto de props não é abstração: dá para imprimi-lo inteiro. Cada atributo — nome na abertura da tag — virou uma chave com seu valor.
function CardLivro(props) {
console.log(props);
return <article>{props.titulo}</article>;
}
renderToStaticMarkup(<CardLivro titulo="Dom Casmurro" autor="Machado de Assis" preco={34.9} esgotado={false} />);O log mostra o objeto antes do return, que exibe só titulo. A saída confirma
as quatro propriedades recebidas. Se algo renderizar errado, inspecionar
props revela o que chegou.
Nos testes, Node.js roda JavaScript fora do navegador, esbuild traduz JSX
e renderToStaticMarkup produz HTML. Não é preciso memorizar essas ferramentas.
Até aqui, você já sabe: props são um objeto de entrada, o pai escolhe os valores e o mesmo filho produz cartões diferentes sem duplicação.
Como desestruturar props no parâmetro?
Escreva entre chaves os campos que o componente precisa. A desestruturação do JavaScript retira propriedades do objeto e cria variáveis com esses nomes.
Assim, você evita repetir props. sem mudar o valor recebido:
function CardLivro({ titulo, autor, preco }) {
return (
<article className="card-livro">
<h3>{titulo}</h3>
<p>{autor}</p>
<strong>R$ {preco.toFixed(2)}</strong>
</article>
);
}
console.log(renderToStaticMarkup(<CardLivro titulo="Dom Casmurro" autor="Machado de Assis" preco={34.9} />));
console.log(renderToStaticMarkup(<CardLivro titulo="Grande Sertão: Veredas" autor="Guimarães Rosa" />));TypeError: Cannot read properties of undefined (reading ‘toFixed’) at CardLivro (file:///private/tmp/livraria-props/out/desestruturar.mjs:9:13) Node.js v24.16.0
Na assinatura, as chaves criam titulo, autor e preco. O retorno usa esses
nomes diretamente. O primeiro log passa os três campos e funciona; o segundo
omite preco, então essa variável recebe undefined, palavra usada pelo
JavaScript para representar a ausência de valor.
Por isso o primeiro card saiu e o segundo parou em preco.toFixed(2):
undefined não possui esse método, uma função ligada ao valor. O rastro aponta
para o componente, a pista útil para corrigir o problema.
A desestruturação tem um preço pequeno e real: você perde o objeto inteiro. Se
quiser os dois, nomeie o resto — function CardLivro({ titulo, ...resto }) — ou
fique com props quando o componente tiver muitos campos opcionais e você
precisar repassá-los.
Quando usar aspas ou chaves em uma prop?
Use aspas para texto literal e chaves para uma expressão JavaScript. Nas chaves,
número continua número, booleano (true ou false) continua booleano e array
continua uma lista de valores.
No HTML, atributos costumam chegar como texto. JSX amplia essa possibilidade, mas a diferença visual pode enganar:
function CardLivro(props) {
console.log('preco:', props.preco, '| typeof:', typeof props.preco);
console.log('paginas:', props.paginas, '| typeof:', typeof props.paginas);
console.log('esgotado:', props.esgotado, '| typeof:', typeof props.esgotado);
return <article>{props.titulo}</article>;
}
renderToStaticMarkup(<CardLivro titulo="Dom Casmurro" preco="34.90" paginas="256" esgotado="false" />);
console.log('---');
renderToStaticMarkup(<CardLivro titulo="Dom Casmurro" preco={34.9} paginas={256} esgotado={false} />);O componente registra cada prop e usa typeof, operador que informa o tipo do
valor. A primeira chamada usa aspas; a segunda usa chaves. Por isso os três
primeiros valores são string — texto — e os seguintes são number ou
boolean.
Repare que esgotado imprime false nos dois casos, embora sejam valores
diferentes. Com aspas, até a formatação do preço quebra:
function CardLivro({ titulo, preco }) {
return (
<article>
<h3>{titulo}</h3>
<strong>R$ {preco.toFixed(2)}</strong>
</article>
);
}
console.log(renderToStaticMarkup(<CardLivro titulo="Dom Casmurro" preco="34.90" />));TypeError: preco.toFixed is not a function at CardLivro (file:///private/tmp/livraria-props/out/aspas-erro.mjs:8:13) Node.js v24.16.0
Repare que a mensagem é diferente da anterior. Cannot read properties of undefined quer dizer “não veio nada”. preco.toFixed is not a function quer
dizer “veio alguma coisa, e essa coisa não sabe fazer isso” — no caso, uma
string.
O esgotado é pior, porque não quebra: ele mente em silêncio. A string
"false" é truthy, tratada como verdadeira numa condição, então qualquer
renderização condicional baseada
nela dá o resultado invertido.
function Selo({ esgotado }) {
return <span>{esgotado && 'ESGOTADO'}</span>;
}
console.log('com aspas: ', renderToStaticMarkup(<Selo esgotado="false" />));
console.log('com chaves:', renderToStaticMarkup(<Selo esgotado={false} />));O livro em estoque apareceu marcado como esgotado. Nenhum erro no console, nenhum aviso: só o cliente desistindo da compra.
Experimente você mesmo: preveja o selo de estoque
Antes de olhar a saída, responda: qual chamada entrega o texto "false", que é
truthy, e qual entrega o booleano false, que é falsy, tratado como falso?
Agora confira qual delas mostra “ESGOTADO”.
Como passar uma prop booleana sem valor?
Escrever o nome da prop sozinho equivale a passar true. Omitir equivale a não
passar nada — e aí ela é undefined, não false:
function CardLivro({ titulo, destaque }) {
console.log('destaque:', destaque, '| typeof:', typeof destaque);
return <article className={destaque ? 'card destaque' : 'card'}>{titulo}</article>;
}
console.log(renderToStaticMarkup(<CardLivro titulo="Torto Arado" destaque />));
console.log(renderToStaticMarkup(<CardLivro titulo="Vidas Secas" />));Na prática dá no mesmo dentro de um if, porque undefined é falsy. Mas se
você comparar com === false, a diferença aparece e o bug custa uma tarde.
Até aqui, você já sabe: aspas criam texto; chaves preservam o tipo; uma prop
booleana escrita sozinha vale true; e uma prop omitida vale undefined.
Como definir valor padrão para uma prop?
Use = durante a desestruturação para escolher o valor adotado quando a prop
for undefined. Esse valor default, ou padrão, é um recurso de parâmetros
do JavaScript, não uma regra exclusiva do React.
Ele evita o primeiro erro deste artigo quando autor ou preço não chegam:
function CardLivro({ titulo, autor = 'Autor desconhecido', preco = 0 }) {
return (
<article className="card-livro">
<h3>{titulo}</h3>
<p>{autor}</p>
<strong>R$ {preco.toFixed(2)}</strong>
</article>
);
}
console.log(renderToStaticMarkup(<CardLivro titulo="Grande Sertão: Veredas" />));
console.log(renderToStaticMarkup(<CardLivro titulo="Quarto de Despejo" autor={undefined} preco={29.9} />));
console.log(renderToStaticMarkup(<CardLivro titulo="O Cortiço" autor={null} preco={0} />));Compare a segunda linha com a terceira. autor={undefined} recebeu o default;
autor={null} não, e o parágrafo saiu vazio. null marca uma ausência
intencional; o default só entra para undefined. É a mesma
regra dos parâmetros de qualquer função, e é o motivo de um card sair sem nome
mesmo com o default no lugar.
Quais valores podem ser passados como props?
Qualquer valor JavaScript pode ser uma prop: texto, número, booleano, array, objeto, JSX ou função. Isso permite entregar tanto dados quanto ações ao filho.
O próximo card recebe um livro, gêneros, uma função que formata o preço e um handler, função executada quando um evento acontece:
function CardLivro({ livro, generos, formatarPreco, aoAdicionar }) {
return (
<article>
<h3>{livro.titulo} ({livro.ano})</h3>
<ul>
{generos.map((g) => <li key={g}>{g}</li>)}
</ul>
<strong>{formatarPreco(livro.preco)}</strong>
<button onClick={aoAdicionar}>Adicionar</button>
</article>
);
}
const emReais = (v) => v.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
console.log(renderToStaticMarkup(
<CardLivro
livro={{ titulo: 'Torto Arado', ano: 2019, preco: 59.9 }}
generos={['Romance', 'Literatura brasileira']}
formatarPreco={emReais}
aoAdicionar={() => console.log('adicionou')}
/>,
));livro fornece título, ano e preço. generos.map transforma cada gênero em
li; formatarPreco recebe o número; e onClick guarda aoAdicionar para o
botão. A função emReais usa toLocaleString para o formato brasileiro. Na
chamada final, cada valor entra entre chaves porque não é texto literal.
Na saída, o espaço entre R$ e 59,90 é U+00A0, usado para o preço não
quebrar de linha. O botão não ganha onclick no HTML: React guarda a função e a
chama quando o evento acontece. Função passada como
prop nunca vira texto publicado.
Como um componente filho envia uma ação ao pai?
O pai passa uma função como prop, e o filho a chama quando a ação acontece. Assim, os dados descem e a intenção volta sem o filho conhecer o carrinho ou a regra do produto.
Este teste cria um DOM com jsdom e dispara o clique com button.click().
DOM é a representação da página manipulada pelo JavaScript. Num projeto com
Vite, o navegador já fornece document; o andaime inicial não é necessário.
import { JSDOM } from 'jsdom';
import { createRoot } from 'react-dom/client';
import { flushSync } from 'react-dom';
const dom = new JSDOM('<div id="raiz"></div>');
globalThis.window = dom.window;
globalThis.document = dom.window.document;
function BotaoComprar({ livro, aoAdicionar }) {
return <button onClick={() => aoAdicionar(livro)}>Comprar {livro.titulo}</button>;
}
function Prateleira() {
const carrinho = [];
const aoAdicionar = (livro) => {
carrinho.push(livro.titulo);
console.log('o pai recebeu:', livro.titulo, '| carrinho:', carrinho);
};
return (
<div>
<BotaoComprar livro={{ titulo: 'Torto Arado' }} aoAdicionar={aoAdicionar} />
<BotaoComprar livro={{ titulo: 'Vidas Secas' }} aoAdicionar={aoAdicionar} />
</div>
);
}
const raiz = createRoot(document.getElementById('raiz'));
flushSync(() => raiz.render(<Prateleira />));
console.log(document.getElementById('raiz').innerHTML);
document.querySelectorAll('button')[1].click(); // clica no segundo botãoOs imports preparam o teste. BotaoComprar recebe livro e função e chama
aoAdicionar(livro) no clique. Prateleira possui carrinho, define a ação que
inclui o título e a entrega aos dois botões.
createRoot monta a tela; flushSync espera; e [1] escolhe o segundo botão.
Assim, “Vidas Secas” chega ao pai. O filho não conhece o carrinho: comunica a
intenção, e o pai decide. Esse fluxo sustenta o useState: estado desce como
prop e a ação volta como chamada de função.
Até aqui, você já sabe: dados descem como props; uma ação volta como chamada de função. O filho comunica “comprar este livro”, e o pai decide o efeito.
Por que não devemos alterar uma prop no componente filho?
Props são entradas somente de leitura: o pai continua sendo o dono do valor. Se o filho altera esse dado, outros componentes podem enxergar uma versão inconsistente e o fluxo deixa de ter uma origem clara.
O erro aparece quando alguém tenta dar desconto mudando props.preco:
function CardLivro(props) {
props.preco = 0; // "dar desconto" mexendo direto na prop
return <strong>R$ {props.preco.toFixed(2)}</strong>;
}
console.log(renderToStaticMarkup(<CardLivro titulo="Dom Casmurro" preco={34.9} />));TypeError: Cannot assign to read only property ‘preco’ of object ‘#<Object>’ at CardLivro (file:///private/tmp/livraria-props/out/somente-leitura.mjs:4:15) Node.js v24.16.0
Esse erro vem do JavaScript. Em desenvolvimento, React aplica Object.freeze,
que congela o objeto. Um módulo ES — arquivo com import ou export — roda em
modo estrito, no qual essa alteração lança TypeError. O teste confirma:
function CardLivro(props) {
console.log('congelado?', Object.isFrozen(props));
const precoComDesconto = props.preco * 0.9; // valor novo, prop intacta
console.log('prop original:', props.preco, '| derivado:', precoComDesconto);
return <strong>R$ {precoComDesconto.toFixed(2)}</strong>;
}
console.log(renderToStaticMarkup(<CardLivro titulo="Dom Casmurro" preco={34.9} />));O caminho é sempre esse: calcule um valor novo, não altere o que chegou. Se o valor precisa mudar de verdade e continuar mudado, ele não era prop — era estado de quem está acima, e a mudança sobe por uma função como a da seção anterior.
Quando usar spread para repassar props?
Use spread entre componentes seus quando o objeto contém exatamente as props esperadas. Evite espalhá-lo diretamente numa tag HTML, pois campos internos podem virar atributos públicos sem intenção.
{...livro} economiza digitação, mas o próximo objeto contém mais dados do que
o componente precisa:
const livro = {
titulo: 'Torto Arado',
autor: 'Itamar Vieira Junior',
preco: 59.9,
estoque: 12,
isbn: '9788571373105',
};
function CardLivro({ titulo, autor, preco, ...resto }) {
return (
<article {...resto}>
<h3>{titulo}</h3>
<p>{autor}</p>
<strong>R$ {preco.toFixed(2)}</strong>
</article>
);
}
console.log(renderToStaticMarkup(<CardLivro {...livro} />));estoque e isbn viraram atributos do HTML publicado. Qualquer pessoa no
inspetor vê o estoque. Com camelCase — palavras unidas com maiúsculas no
meio — ou objetos aninhados, o vazamento fica mais explícito:
const livro = {
titulo: 'Torto Arado',
precoDeCusto: 31.5,
fornecedor: { nome: 'Distribuidora Sul' },
};
function CardLivro({ titulo, ...resto }) {
return <article {...resto}>{titulo}</article>;
}
console.log(renderToStaticMarkup(<CardLivro {...livro} />));O React avisou, e mesmo assim mandou tudo para o HTML — inclusive o preço de
custo e um [object Object] no lugar do fornecedor. Use spread para repassar
props entre componentes seus; para o elemento do DOM final, escreva os atributos
na mão.
Como escolher bons nomes para props?
Nomeie a prop pela informação ou ação necessária ao componente, não pela origem do dado nem pelo detalhe visual usado hoje. Bons nomes reduzem acoplamento e deixam o componente útil em mais contextos.
Acoplamento é a dependência de uma peça em detalhes de outra. Quanto mais o card sabe sobre API, página ou banco, menos lugares conseguem reaproveitá-lo.
| em vez de | prefira | por quê |
|---|---|---|
<Card livro={livro} /> para exibir dois campos |
<Card titulo={...} preco={...} /> |
o componente passa a servir para qualquer coisa que tenha título e preço |
<Card corDoFundo="#0f0" /> |
<Card variante="destaque" /> |
quem chama escolhe a intenção; o CSS fica com o componente |
<Card onClick={...} /> num componente seu |
<Card aoAdicionar={...} /> |
onClick promete um evento do DOM; o nome do domínio promete a ação |
<Card mostrarBotao={true} escondido={false} /> |
<Card comBotao /> |
duas booleanas que se contradizem geram quatro estados, e dois deles não existem |
A regra é: a prop diz o que o componente precisa saber, nunca de onde o dado
veio. titulo é bom; tituloDaApi amarra o card à origem e deixa de servir
quando o campo vier de outro lugar.
Juntando tudo, a prateleira fica assim — o pai lê o catálogo, escolhe o que cada card precisa e passa item por item:
const catalogo = [
{ id: 1, titulo: 'Torto Arado', autor: 'Itamar Vieira Junior', preco: 59.9, destaque: true },
{ id: 2, titulo: 'O Cortiço', autor: 'Aluísio Azevedo', preco: 24.5, destaque: false },
];
function CardLivro({ titulo, autor, preco, destaque = false }) {
return (
<article className={destaque ? 'card destaque' : 'card'}>
<h3>{titulo}</h3>
<p>{autor}</p>
<strong>R$ {preco.toFixed(2)}</strong>
</article>
);
}
function Prateleira({ titulo, livros }) {
return (
<section>
<h2>{titulo}</h2>
{livros.map((livro) => (
<CardLivro
key={livro.id}
titulo={livro.titulo}
autor={livro.autor}
preco={livro.preco}
destaque={livro.destaque}
/>
))}
</section>
);
}
console.log(renderToStaticMarkup(<Prateleira titulo="Literatura brasileira" livros={catalogo} />));catalogo guarda dois livros. CardLivro recebe apenas os quatro valores que
usa; o default de destaque é false, e o ternário escolhe a classe CSS.
Prateleira recebe título e livros, cria um card por item com map e passa cada
prop de forma explícita. A última linha monta a seção e produz o HTML da saída.
O key não é uma prop comum: ele não chega no objeto de props do CardLivro.
É um dado que o React usa para identificar cada item da
lista renderizada com map.
O caminho mínimo → produto está completo: começamos com um card que recebe três valores e terminamos com uma prateleira que distribui dados de catálogo para vários componentes, sem copiar sua estrutura.
Até aqui, você já sabe: props configuram componentes, preservam tipos entre chaves, aceitam funções, não devem ser alteradas e precisam expor apenas o que o filho realmente usa.
O que estudar depois de props no React?
A próxima lição é renderização condicional no
React: ela mostra como props como
esgotado e destaque decidem o que aparece. Depois, a trilha passa por listas
e eventos até o useState, que guarda valores mutáveis.
Mais adiante, a trilha de TypeScript ensina o editor
a avisar sobre tipos antes da execução. O guia de
React mostra a sequência completa.
Prefere aprender em vídeo?
Tem uma aula sobre este assunto no nosso canal.
Perguntas frequentes
Qual a diferença entre props e state?
Como levar uma prop até o neto sem passar pelo filho?
Preciso declarar quais props o componente aceita?
Posso passar um componente inteiro como prop?
<CardLivro rodape={<BotaoComprar />} /> é legítimo e é a base dos componentes de layout, que recebem pedaços de tela prontos e só decidem onde encaixar.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 com React 19.2.8, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- React — Passando props para um componente — pt-br.react.dev
- React 19 — Removed: propTypes — react.dev
- MDN — Atribuição via desestruturação — developer.mozilla.org



