threlte logo
@threlte/extras

<Text>

The <Text> component uses troika-three-text to render text.

<script lang="ts">
  import { Canvas } from '@threlte/core'
  import Scene from './Scene.svelte'
  import { Color, List, Pane, Slider, Text } from 'svelte-tweakpane-ui'

  const anchorXOptions = {
    left: 'left',
    center: 'center',
    right: 'right'
  } as const

  const anchorYOptions = {
    top: 'top',
    'top-baseline': 'top-baseline',
    middle: 'middle',
    'bottom-baseline': 'bottom-baseline',
    bottom: 'bottom'
  } as const

  const directionOptions = {
    auto: 'auto',
    ltr: 'ltr',
    rtl: 'rtl'
  } as const

  const textAlignOptions = {
    left: 'left',
    right: 'right',
    center: 'center',
    justify: 'justify'
  } as const

  const whiteSpaceOptions = {
    normal: 'normal',
    nowrap: 'nowrap',
    'pre-wrap': 'pre-wrap'
  } as const

  const overflowWrapOptions = {
    normal: 'normal',
    'break-word': 'break-word'
  } as const

  let options = $state({
    text: 'hello world',
    fontSize: 1,
    maxWidth: 20,
    letterSpacing: -0.1,
    lineHeight: 1.15,
    textIndent: 0,
    textAlign: 'center' as keyof typeof textAlignOptions,
    whiteSpace: 'normal' as keyof typeof whiteSpaceOptions,
    overflowWrap: 'normal' as keyof typeof overflowWrapOptions,
    direction: 'auto' as keyof typeof directionOptions,
    anchorX: 'center' as keyof typeof anchorXOptions,
    anchorY: 'middle' as keyof typeof anchorYOptions,
    curveRadius: 0,
    color: '#ffffff',
    fillOpacity: 1,
    outlineWidth: 0,
    outlineColor: '#000000',
    outlineOpacity: 1,
    outlineBlur: 0,
    outlineOffsetX: 0,
    outlineOffsetY: 0,
    strokeWidth: 0,
    strokeColor: '#808080',
    strokeOpacity: 1
  })
</script>

<Pane
  title="Text"
  position="fixed"
>
  <Text
    bind:value={options.text}
    label="text"
  />

  <Slider
    bind:value={options.fontSize}
    label="fontSize"
    min={0.1}
    max={4}
    step={0.1}
  />
  <Slider
    bind:value={options.maxWidth}
    label="maxWidth"
    min={1}
    max={40}
  />
  <Slider
    bind:value={options.letterSpacing}
    label="letterSpacing"
    min={-0.2}
    max={0.5}
    step={0.01}
  />
  <Slider
    bind:value={options.lineHeight}
    label="lineHeight"
    min={0.5}
    max={2.5}
    step={0.05}
  />
  <Slider
    bind:value={options.textIndent}
    label="textIndent"
    min={0}
    max={5}
    step={0.1}
  />
  <List
    bind:value={options.textAlign}
    label="textAlign"
    options={textAlignOptions}
  />
  <List
    bind:value={options.whiteSpace}
    label="whiteSpace"
    options={whiteSpaceOptions}
  />
  <List
    bind:value={options.overflowWrap}
    label="overflowWrap"
    options={overflowWrapOptions}
  />
  <List
    bind:value={options.direction}
    label="direction"
    options={directionOptions}
  />

  <List
    bind:value={options.anchorX}
    label="anchorX"
    options={anchorXOptions}
  />
  <List
    bind:value={options.anchorY}
    label="anchorY"
    options={anchorYOptions}
  />

  <Slider
    bind:value={options.curveRadius}
    label="curveRadius"
    min={-10}
    max={10}
    step={0.1}
  />

  <Color
    bind:value={options.color}
    label="color"
  />
  <Slider
    bind:value={options.fillOpacity}
    label="fillOpacity"
    min={0}
    max={1}
    step={0.01}
  />

  <Slider
    bind:value={options.outlineWidth}
    label="outlineWidth"
    min={0}
    max={0.5}
    step={0.01}
  />
  <Color
    bind:value={options.outlineColor}
    label="outlineColor"
  />
  <Slider
    bind:value={options.outlineOpacity}
    label="outlineOpacity"
    min={0}
    max={1}
    step={0.01}
  />
  <Slider
    bind:value={options.outlineBlur}
    label="outlineBlur"
    min={0}
    max={0.5}
    step={0.01}
  />
  <Slider
    bind:value={options.outlineOffsetX}
    label="outlineOffsetX"
    min={-0.5}
    max={0.5}
    step={0.01}
  />
  <Slider
    bind:value={options.outlineOffsetY}
    label="outlineOffsetY"
    min={-0.5}
    max={0.5}
    step={0.01}
  />

  <Slider
    bind:value={options.strokeWidth}
    label="strokeWidth"
    min={0}
    max={0.5}
    step={0.01}
  />
  <Color
    bind:value={options.strokeColor}
    label="strokeColor"
  />
  <Slider
    bind:value={options.strokeOpacity}
    label="strokeOpacity"
    min={0}
    max={1}
    step={0.01}
  />
</Pane>

<div>
  <Canvas>
    <Scene {...options} />
  </Canvas>
</div>

<style>
  div {
    height: 100%;
    background-color: rgb(254 61 0 / 0.2);
  }
</style>
<script lang="ts">
  import { T } from '@threlte/core'
  import { Grid, OrbitControls, Text } from '@threlte/extras'

  interface Props {
    text: string
    fontSize: number
    maxWidth: number
    letterSpacing: number
    lineHeight: number
    textIndent: number
    textAlign: 'left' | 'right' | 'center' | 'justify'
    whiteSpace: 'normal' | 'nowrap' | 'pre-wrap'
    overflowWrap: 'normal' | 'break-word'
    anchorX: 'left' | 'center' | 'right'
    anchorY: 'top' | 'top-baseline' | 'middle' | 'bottom-baseline' | 'bottom'
    curveRadius: number
    color: string
    fillOpacity: number
    outlineWidth: number
    outlineColor: string
    outlineOpacity: number
    outlineBlur: number
    outlineOffsetX: number
    outlineOffsetY: number
    strokeWidth: number
    strokeColor: string
    strokeOpacity: number
  }

  let { ...textProps }: Props = $props()
</script>

<T.OrthographicCamera
  zoom={80}
  position={[0, 5, 10]}
  makeDefault
  oncreate={(ref) => {
    ref.lookAt(0, 0, 0)
  }}
/>

<OrbitControls
  autoRotate
  enableDamping
  enableZoom={false}
  autoRotateSpeed={0.3}
/>

<Text
  position.y={0.5}
  {...textProps}
/>

<Grid sectionColor="#FF3E00" />

Example

<script>
  import { Text } from '@threlte/extras'
  let value = ''
</script>

<input
  type="text"
  bind:value
/>
<Text text={value} />

<Text> is suspense-ready and will suspend while the font is loading. You can use the characters prop to preload a specific set of characters to prevent FOUC.

<script>
  import { Text, Suspense } from '@threlte/extras'
</script>

<Suspense>
  <Text
    text="HELLO WORLD"
    characters="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  />

  {#snippet fallback()}
    <!-- show fallback content while font data is loading -->
  {/snippet}
</Suspense>

Component Signature

<Text> extends < T . Mesh > and supports all its props, snippets, bindings and events.

Props

name
type
required
description

anchorX
number | 'left' | 'center' | 'right' | string
no

anchorY
number | 'top' | 'top-baseline' | 'middle' | 'bottom-baseline' | 'bottom' | string
no

characters
string
no
The set of characters `<Text>` will preload while suspended.

clipRect
[number, number, number, number]
no

color
string | number | Color
no

curveRadius
number
no

depthOffset
number
no

direction
'auto' | 'ltr' | 'rtl'
no

fillOpacity
number
no

font
string
no

fontSize
number
no

glyphGeometryDetail
number
no

letterSpacing
number
no

lineHeight
number | string
no

material
Material | Material[]
no

maxWidth
number
no

outlineBlur
number | string
no

outlineColor
ColorRepresentation
no

outlineOffsetX
number | string
no

outlineOffsetY
number | string
no

outlineOpacity
number
no

outlineWidth
number | string
no

overflowWrap
'normal' | 'break-word' | 'normal'
no

sdfGlyphSize
number
no

strokeColor
ColorRepresentation | Color
no

strokeOpacity
number
no

strokeWidth
number | string
no

text
string
no

textAlign
'left' | 'right' | 'center' | 'justify'
no

textIndent
number
no

whiteSpace
'normal' | 'nowrap' | 'pre-wrap'
no