Pular para o conteúdo principal

Radio

O componente Radio é um elemento de seleção única que permite ao usuário escolher uma opção de um grupo de alternativas mutuamente exclusivas. É ideal para formulários que requerem uma única seleção entre múltiplas opções.

Importação

import { Radio } from '@useblu/ocean-react';

Importação específica (recomendado para tree-shaking)

import { Radio } from '@useblu/ocean-react/Radio';

Importação de tipos TypeScript

import type { RadioProps } from '@useblu/ocean-react';

// Uso em componente customizado
const MyRadioGroup: React.FC<{
options: string[];
value: string;
onChange: (value: string) => void;
}> = ({ options, value, onChange }) => {
return (
<div>
{options.map((option) => (
<Radio
key={option}
label={option}
name="custom-group"
value={option}
checked={value === option}
onChange={(e) => onChange(e.target.value)}
/>
))}
</div>
);
};

Playground Interativo

Explore o componente Radio no playground interativo do Storybook:

Documentação Completa

Para documentação técnica detalhada, exemplos de uso e API completa, consulte o Storybook do Radio.

No Storybook você encontrará:

  • Controles interativos para testar todas as props
  • API gerada automaticamente com tipagem completa
  • Exemplos visuais de todas as variantes e estados
  • Playground para experimentação rápida

Uso básico

<Radio label="Opção selecionada" checked />

Grupo de Radios

Exemplo de grupo de radios interativo:

Estados

O Radio suporta diferentes estados visuais:

Sem label

Você pode usar o Radio sem label:

Estado de erro

O Radio suporta estado de erro com mensagem de validação exibida verticalmente abaixo do label:

<Radio label="Opção com erro" error errorMessage="Esta opção é obrigatória" />

Exemplo de validação em grupo

function RadioGroupValidation() {
const [selected, setSelected] = React.useState('');
const [touched, setTouched] = React.useState(false);
const hasError = touched && !selected;

return (
<div>
<h3>Selecione uma opção:</h3>
<Radio
label="Opção 1"
name="validation-group"
value="option1"
checked={selected === 'option1'}
onChange={(e) => setSelected(e.target.value)}
onBlur={() => setTouched(true)}
error={hasError}
/>
<Radio
label="Opção 2"
name="validation-group"
value="option2"
checked={selected === 'option2'}
onChange={(e) => setSelected(e.target.value)}
onBlur={() => setTouched(true)}
error={hasError}
/>
<Radio
label="Opção 3"
name="validation-group"
value="option3"
checked={selected === 'option3'}
onChange={(e) => setSelected(e.target.value)}
onBlur={() => setTouched(true)}
error={hasError}
errorMessage={
hasError ? 'Selecione uma opção para continuar' : undefined
}
/>
</div>
);
}

Exemplo de formulário

Exemplo prático de uso em formulário:

API

Props

PropTipoPadrãoDescrição
labelReactNode-Texto do label do radio
checkedbooleanfalseDefine se o radio está selecionado
disabledbooleanfalseDesabilita o radio
errorbooleanfalseDefine se o radio está em estado de erro
errorMessagestring-Mensagem de erro exibida abaixo do label
namestring-Nome do grupo de radios
valuestring-Valor do radio
onChange(event: ChangeEvent) => void-Função chamada quando o valor muda
idstring-ID único do radio
classNamestring-Classes CSS adicionais

Classes CSS

O componente utiliza as seguintes classes CSS que podem ser customizadas:

ClasseDescrição
.ods-radio__root-containerContainer wrapper do Radio
.ods-radio__rootContainer do label e input
.ods-radioElemento input do radio
.ods-radio__checkmarkElemento visual do radio
.ods-radio__checkmark--errorModificador de erro no checkmark
.ods-radio__labelLabel do radio
.ods-radio__error-messageMensagem de erro abaixo do label

Acessibilidade

  • ✅ Suporte completo a navegação por teclado
  • ✅ Estados de foco visíveis
  • ✅ Atributos ARIA apropriados
  • ✅ Suporte a screen readers
  • ✅ Contraste adequado em todos os estados

Boas práticas

  • Use name igual para todos os radios do mesmo grupo
  • Use value único para cada radio do grupo
  • Use id único para cada radio
  • Use labels descritivos que expliquem a opção
  • Use aria-label quando não houver label visível

Melhores práticas

✅ Faça

  • Use para seleção única entre múltiplas opções
  • Sempre agrupe radios relacionados com o mesmo name
  • Use labels claros e descritivos
  • Use valores únicos para cada radio
  • Use estado disabled quando apropriado
  • Organize radios verticalmente para melhor legibilidade
  • Use error e errorMessage para feedback de validação
  • Exiba a mensagem de erro apenas no último radio do grupo

❌ Não faça

  • Não use para seleção múltipla (use Checkbox)
  • Não use o mesmo name para grupos diferentes
  • Não use valores duplicados no mesmo grupo
  • Não use labels vagos ou genéricos
  • Não ignore estados de foco e hover
  • Não use para ações (use Button)
  • Não exiba múltiplas mensagens de erro para o mesmo grupo

Casos de uso comuns

Seleção de gênero

function GenderSelection() {
const [gender, setGender] = React.useState('');

return (
<div>
<h3>Selecione seu gênero:</h3>
<Radio
label="Feminino"
name="gender"
value="female"
checked={gender === 'female'}
onChange={(e) => setGender(e.target.value)}
/>
<Radio
label="Masculino"
name="gender"
value="male"
checked={gender === 'male'}
onChange={(e) => setGender(e.target.value)}
/>
<Radio
label="Não binário"
name="gender"
value="non-binary"
checked={gender === 'non-binary'}
onChange={(e) => setGender(e.target.value)}
/>
</div>
);
}

Seleção de plano

function PlanSelection() {
const [selectedPlan, setSelectedPlan] = React.useState('basic');

return (
<div>
<h3>Escolha seu plano:</h3>
<Radio
label="Plano Básico - R$ 9,90/mês"
name="plan"
value="basic"
checked={selectedPlan === 'basic'}
onChange={(e) => setSelectedPlan(e.target.value)}
/>
<Radio
label="Plano Premium - R$ 19,90/mês"
name="plan"
value="premium"
checked={selectedPlan === 'premium'}
onChange={(e) => setSelectedPlan(e.target.value)}
/>
<Radio
label="Plano Enterprise - R$ 49,90/mês"
name="plan"
value="enterprise"
checked={selectedPlan === 'enterprise'}
onChange={(e) => setSelectedPlan(e.target.value)}
/>
</div>
);
}

Configurações de notificação

function NotificationSettings() {
const [notificationType, setNotificationType] = React.useState('email');

return (
<div>
<h3>Como você prefere receber notificações?</h3>
<Radio
label="Email"
name="notification"
value="email"
checked={notificationType === 'email'}
onChange={(e) => setNotificationType(e.target.value)}
/>
<Radio
label="SMS"
name="notification"
value="sms"
checked={notificationType === 'sms'}
onChange={(e) => setNotificationType(e.target.value)}
/>
<Radio
label="Push notification"
name="notification"
value="push"
checked={notificationType === 'push'}
onChange={(e) => setNotificationType(e.target.value)}
/>
<Radio
label="Não receber notificações"
name="notification"
value="none"
checked={notificationType === 'none'}
onChange={(e) => setNotificationType(e.target.value)}
/>
</div>
);
}

Seleção de idioma

function LanguageSelection() {
const [language, setLanguage] = React.useState('pt-BR');

return (
<div>
<h3>Selecione seu idioma:</h3>
<Radio
label="Português (Brasil)"
name="language"
value="pt-BR"
checked={language === 'pt-BR'}
onChange={(e) => setLanguage(e.target.value)}
/>
<Radio
label="English (US)"
name="language"
value="en-US"
checked={language === 'en-US'}
onChange={(e) => setLanguage(e.target.value)}
/>
<Radio
label="Español"
name="language"
value="es"
checked={language === 'es'}
onChange={(e) => setLanguage(e.target.value)}
/>
</div>
);
}

Radios desabilitados

function DisabledRadios() {
return (
<div>
<h3>Opções disponíveis:</h3>
<Radio
label="Opção disponível"
name="disabled-example"
value="available"
/>
<Radio
label="Opção indisponível"
name="disabled-example"
value="unavailable"
disabled
/>
<Radio
label="Opção selecionada"
name="disabled-example"
value="selected"
checked
/>
<Radio
label="Opção selecionada desabilitada"
name="disabled-example"
value="selected-disabled"
checked
disabled
/>
</div>
);
}

Radios sem label

function RadiosWithoutLabel() {
return (
<div>
<h3>Selecione uma opção:</h3>
<div>
<Radio name="no-label" value="option1" aria-label="Opção 1" />
<Radio name="no-label" value="option2" checked aria-label="Opção 2" />
<Radio name="no-label" value="option3" disabled aria-label="Opção 3" />
</div>
</div>
);
}