import { Text } from '@vega-ui/react';
<Text size={3}>The quick brown fox jumps over the lazy dog.</Text>- Browser support
- Widely available
- Chrome 111
- Edge 111
- Firefox 113
- Safari 15.4
- Safari on iOS 15.4
- Bundle size
- Oklab and OkLCh — comes from the design tokens
- :where()
Summary
Text is the base body-typography primitive. It controls size and weight while allowing semantic composition through asChild.
Imports
import { Text, type TextProps, type TextSize } from '@vega-ui/react';Exported Types
TextPropsTextSize = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11
Basic Usage
import { Text } from '@vega-ui/react';
export default function BasicUsage() {
return (
<Text asChild size={3}>
<p style={{ maxWidth: '60ch' }}>
Your trial ends in 5 days. Add a payment method to keep your projects, history and integrations.
</p>
</Text>
);
}Variants
- Size:
1through11 - Weight:
400,500,600,700,900 - Composition: default element or
asChild
Edge Cases
asChildchanges the rendered element but keeps the text styling contract.white-space: pre-linepreserves newline breaks from content strings.- Very large text sizes should still be checked against layout and line length.
Common Mistakes
- Using
Textwhere document headings should be semanticHeading. - Overriding weight and size ad hoc instead of using the design scale.
- Forgetting that multiline content will preserve line breaks.
Examples
Basic
Basic: article body
Text renders a <span> by default; paragraphs use asChild with a <p> so the copy keeps paragraph semantics. A nested Text with fontWeight emphasizes a fragment, and the byline is muted through --t-color.
import { Heading, Text } from '@vega-ui/react';
import type { CSSProperties } from 'react';
export default function BodyCopy() {
return (
<article style={{ display: 'grid', gap: 'var(--spacing-6)', width: '100%', maxWidth: '60ch' }}>
<header style={{ display: 'grid', gap: 'var(--spacing-2)', marginBlockEnd: 'var(--spacing-2)' }}>
<Heading as='h3' size={6}>Scheduled maintenance on March 18</Heading>
<Text size={2} style={{ '--t-color': 'var(--label-secondary)' } as CSSProperties}>
Posted by the Platform team
</Text>
</header>Controlled/Stateful
Controlled/Stateful: copy derived from state
The sentence is computed from the seat count with Intl.PluralRules and switches to a different message at zero. aria-live='polite' announces the change after each click without moving focus.
import { Button, Text } from '@vega-ui/react';
import { useState } from 'react';
const totalSeats = 5;
const plural = new Intl.PluralRules('en-US');
export default function DynamicHelperCopy() {
const [assigned, setAssigned] = useState(3);
const remaining = totalSeats - assigned;
const seatWord = plural.select(remaining) === 'one' ? 'seat' : 'seats';
return (Form/Integration
Form/Integration: hint and character counter
Both the hint and the live counter are Text elements referenced from the input with aria-describedby, so screen readers read them with the field. The value is submitted with FormData, and reset clears the counter together with the input.
import { Button, Label, Text, TextField, TextFieldInput } from '@vega-ui/react';
import { useState, type CSSProperties, type FormEvent } from 'react';
const maxLength = 40;
export default function HelperTextBelowAField() {
const [headline, setHeadline] = useState('');
const [saved, setSaved] = useState('');
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);Layout/Overlay
Layout/Overlay: description in a dialog
The dialog takes its accessible name from the heading and its description from the Text paragraph through aria-labelledby and aria-describedby, so screen readers announce the consequences as soon as the dialog opens.
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogTrigger,
Heading,
Icon,
IconButton,
Text,
} from '@vega-ui/react';
import { XIcon } from '@vega-ui/icons';Error
Error: inline validation message
The coupon is checked on submit. An unknown code shows a Text message in the error color, links it with aria-describedby and sets aria-invalid; editing the field clears the error. Color is not the only signal: the message itself says what is wrong.
import { Button, Label, Text, TextField, TextFieldInput } from '@vega-ui/react';
import { useState, type CSSProperties, type FormEvent } from 'react';
const validCodes = ['SPRING20', 'WELCOME10'];
export default function InlineValidationCopy() {
const [error, setError] = useState('');
const [applied, setApplied] = useState('');
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const data = new FormData(event.currentTarget);Disabled
Disabled: explanation for a disabled section
The whole Fieldset is disabled and a muted Text explains why. The explanation is linked to the fieldset with aria-describedby, because disabled controls cannot receive focus and a nearby note is easy to miss.
import { Button, Fieldset, FieldsetLegend, Label, Text, TextField, TextFieldInput } from '@vega-ui/react';
import type { CSSProperties } from 'react';
export default function MutedExplanatoryText() {
return (
<div style={{ display: 'grid', gap: 'var(--spacing-8)', width: '100%' }}>
<Fieldset disabled aria-describedby='export-locked-note' style={{ display: 'grid', gap: 'var(--spacing-12)' }}>
<FieldsetLegend style={{ marginBlockEnd: 'var(--spacing-8)' }}>Scheduled exports</FieldsetLegend>
<div style={{ display: 'grid', gap: 'var(--spacing-3)' }}>
<Label htmlFor='export-email'>Send CSV to</Label>
<TextField>
<TextFieldInput id='export-email' name='exportEmail' type='email' defaultValue='finance@acme.io' />Edge
Edge: newlines in data
Text uses white-space: pre-line: newlines in a string become line breaks, which suits an address. Hard-wrapped copy from a CMS renders the same way, so override it with whiteSpace: 'normal' when the newlines are not meaningful.
import { Text } from '@vega-ui/react';
const address = 'Acme GmbH\nTorstraße 140\n10119 Berlin\nGermany';
const description = 'Soft merino wool sweater with a relaxed fit,\nribbed cuffs and a crew neck.\nMachine washable at 30\u00a0°C.';
export default function MultilineContent() {
return (
<div style={{ display: 'grid', gap: 'var(--spacing-8)', width: '100%' }}>
<Text size={3}>{address}</Text>
<Text size={3} style={{ whiteSpace: 'normal' }}>{description}</Text>
</div>
);Edge: long text in a narrow column
Truncation needs a block box: Text is an inline <span>, so textOverflow: 'ellipsis' works only together with display: 'block', overflow: 'hidden' and whiteSpace: 'nowrap'; title keeps the full name available. Long unbroken strings such as emails wrap with overflowWrap: 'anywhere'.
import { Text } from '@vega-ui/react';
const files = [
'Q1-2026-board-presentation-final-v7-approved-by-legal.pdf',
'customer-interviews-transcripts-berlin-and-lisbon.docx',
'logo.svg',
];
export default function LongTextOverflow() {
return (
<div style={{ display: 'grid', gap: 'var(--spacing-4)', width: '100%' }}>
{files.map((file) => (Edge: right-to-left names in a sentence
User names in Hebrew or Arabic inside an English sentence can reorder the neighbouring numbers and punctuation. Wrapping the name in <bdi> through asChild isolates its direction.
import { Text } from '@vega-ui/react';
const comments = [
{ author: 'Maya Cohen', handle: 'מאיה כהן', count: 3 },
{ author: 'Omar Haddad', handle: 'عمر حداد', count: 12 },
];
export default function BidiUserContent() {
return (
<div style={{ display: 'grid', gap: 'var(--spacing-4)' }}>
{comments.map((comment) => (
<Text key={comment.author} size={3}>Anatomy
Overview
Text is a single-part typography primitive. The root owns the full public contract for body copy sizing, weight, and semantic composition.
Required Parts
Text
Required. Renders the text node with VegaUI body typography styles.
Optional Parts
There are no exported child parts.
Composition Order
Text- content or
asChildtarget
Valid Composition Patterns
The outer Text renders a <p> through asChild; inline Text elements inside it stay <span>s or render semantic elements such as <time>.
import { Text } from '@vega-ui/react';
export default function ValidCompositionPatterns() {
return (
<Text asChild size={3}>
<p>
Order <Text fontWeight={700}>#4821</Text> ships on{' '}
<Text asChild>
<time dateTime='2026-03-14'>March 14</time>
</Text>
.
</p>Invalid Composition Patterns
Text used as a heading replacement
This may look correct visually but loses heading semantics.
Arbitrary raw CSS used instead of the size scale
This creates drift from the shared typography system.