or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-visx--text

Enhanced SVG Text component with word-wrapping, vertical alignment, rotation, and scale-to-fit capabilities for React visualizations

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@visx/text@3.12.x

To install, run

npx @tessl/cli install tessl/npm-visx--text@3.12.0

index.mddocs/

@visx/text

@visx/text provides an enhanced SVG Text component for React applications that extends the basic SVG text element with advanced text layout capabilities including automatic word-wrapping when width is specified, vertical alignment through the verticalAnchor prop, text rotation via the angle prop, and scale-to-fit functionality.

Package Information

  • Package Name: @visx/text
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @visx/text

Core Imports

import { Text, useText, getStringWidth } from "@visx/text";
import type { TextProps, WordsWithWidth, compareFunction } from "@visx/text";

For CommonJS:

const { Text, useText, getStringWidth } = require("@visx/text");

Basic Usage

import React from "react";
import { Text } from "@visx/text";

// Simple text with vertical alignment
const App = () => (
  <svg width={400} height={300}>
    <Text 
      x={50} 
      y={50} 
      verticalAnchor="start"
      fontSize={16}
      fill="black"
    >
      Hello world
    </Text>
  </svg>
);

// Text with word wrapping
const WrappedTextExample = () => (
  <svg width={400} height={300}>
    <Text 
      x={50} 
      y={50} 
      width={200}
      verticalAnchor="start"
      fontSize={14}
      fill="blue"
    >
      This is a long text that will be wrapped automatically when the width constraint is applied.
    </Text>
  </svg>
);

// Rotated and scaled text
const AdvancedTextExample = () => (
  <svg width={400} height={300}>
    <Text 
      x={200} 
      y={150} 
      width={180}
      angle={45}
      scaleToFit="shrink-only"
      textAnchor="middle"
      verticalAnchor="middle"
      fontSize={16}
      fill="red"
    >
      Rotated and fitted text
    </Text>
  </svg>
);

Architecture

@visx/text is built around three core components:

  • Text Component: The main React component that renders enhanced SVG text elements with advanced layout features
  • useText Hook: Internal hook that handles all text layout calculations including line breaking, positioning, and transforms
  • getStringWidth Utility: Browser-based text measurement function using hidden SVG elements for accurate pixel width calculations

The architecture follows a separation of concerns where the Text component handles rendering while useText manages all layout computation, and getStringWidth provides the foundational measurement capabilities.

Capabilities

Text Component

The main React component providing enhanced SVG text rendering with word-wrapping, alignment, rotation, and scaling features.

/**
 * Enhanced SVG Text component with advanced layout features
 * @param props - TextProps configuration object
 * @returns JSX.Element - SVG element containing positioned text
 */
function Text(props: TextProps): JSX.Element;

type TextOwnProps = {
  /** CSS class name to apply to the SVGText element */
  className?: string;
  /** Whether to scale font size to fit the specified width (default: false) */
  scaleToFit?: boolean | 'shrink-only';
  /** Rotation angle in degrees */
  angle?: number;
  /** Horizontal text anchor position (default: 'start') */
  textAnchor?: 'start' | 'middle' | 'end' | 'inherit';
  /** Vertical text anchor position (default: 'end' in useText hook, undefined in Text component) */
  verticalAnchor?: 'start' | 'middle' | 'end';
  /** Inline styles for the text (used in text measurement calculations) */
  style?: React.CSSProperties;
  /** Ref for the SVG container element */
  innerRef?: React.Ref<SVGSVGElement>;
  /** Ref for the text element */
  innerTextRef?: React.Ref<SVGTextElement>;
  /** X position of the text */
  x?: string | number;
  /** Y position of the text */
  y?: string | number;
  /** X offset from the position (default: 0) */
  dx?: string | number;
  /** Y offset from the position (default: 0) */
  dy?: string | number;
  /** Line height for multi-line text (default: '1em') */
  lineHeight?: React.SVGAttributes<SVGTSpanElement>['dy'];
  /** Cap height for vertical alignment calculations (default: '0.71em') */
  capHeight?: React.SVGAttributes<SVGTSpanElement>['capHeight'];
  /** Font size */
  fontSize?: string | number;
  /** Font family */
  fontFamily?: string;
  /** Text fill color */
  fill?: string;
  /** Maximum width for word wrapping (approximate, words are not split) */
  width?: number;
  /** Text content to render */
  children?: string | number;
};

type TextProps = TextOwnProps & Omit<React.SVGAttributes<SVGTextElement>, keyof TextOwnProps>;

/**
 * Note: TextProps extends all standard SVG text element attributes including:
 * - Standard SVG styling: stroke, strokeWidth, opacity, etc.
 * - Event handlers: onClick, onMouseOver, onMouseOut, etc.
 * - Accessibility attributes: aria-*, role, etc.
 * Only the custom @visx/text-specific props are documented above.
 */

Usage Examples:

import React from "react";
import { Text } from "@visx/text";

// Basic text positioning
<Text x={100} y={50} fontSize={16} fill="black">
  Simple text
</Text>

// Word-wrapped text
<Text 
  x={50} 
  y={50} 
  width={200} 
  fontSize={14}
  verticalAnchor="start"
  lineHeight="1.2em"
>
  This text will wrap to multiple lines when it exceeds 200 pixels width
</Text>

// Rotated text with scaling
<Text 
  x={150} 
  y={100} 
  width={100}
  angle={30}
  scaleToFit="shrink-only"
  textAnchor="middle"
  verticalAnchor="middle"
  fontSize={16}
>
  Rotated scaled text
</Text>

// Styled text with refs and standard SVG attributes
<Text 
  x={200} 
  y={150}
  className="custom-text"
  style={{ fontWeight: 'bold' }}
  innerRef={svgRef}
  innerTextRef={textRef}
  fill="#ff6b6b"
  stroke="black"
  strokeWidth={0.5}
  opacity={0.9}
  onClick={(e) => console.log('Text clicked')}
>
  Styled text with refs
</Text>

Text Layout Hook

React hook that handles text layout calculations including line breaking, vertical positioning, and transform generation.

/**
 * Hook for calculating text layout including line breaks and positioning
 * @param props - TextProps configuration
 * @returns Object containing layout calculation results
 */
function useText(props: TextProps): {
  wordsByLines: WordsWithWidth[];
  startDy: string;
  transform: string;
};

interface WordsWithWidth {
  /** Array of words in this line */
  words: string[];
  /** Calculated pixel width of the line (optional) */
  width?: number;
}

Usage Example:

import React from "react";
import { useText } from "@visx/text";
import type { TextProps } from "@visx/text";

const CustomTextComponent = (props: TextProps) => {
  const { wordsByLines, startDy, transform } = useText(props);
  const { x = 0, fontSize, textAnchor = 'start' } = props;
  
  return (
    <svg>
      <text transform={transform} fontSize={fontSize} textAnchor={textAnchor}>
        {wordsByLines.map((line, index) => (
          <tspan 
            key={index} 
            x={x} 
            dy={index === 0 ? startDy : props.lineHeight || '1em'}
          >
            {line.words.join(' ')}
          </tspan>
        ))}
      </text>
    </svg>
  );
};

String Width Measurement

Utility function for measuring the pixel width of text strings using browser SVG measurement.

/**
 * Measures the pixel width of a text string using SVG measurement
 * Memoized using lodash.memoize with cache key: `${str}_${JSON.stringify(style)}`
 * Creates a hidden SVG element with id '__react_svg_text_measurement_id' for measurements
 * @param str - Text string to measure
 * @param style - Optional CSS style object to apply during measurement
 * @returns Pixel width of the text using getComputedTextLength(), or null if measurement fails (non-browser environment, DOM errors)
 */
function getStringWidth(str: string, style?: object): number | null;

Usage Example:

import { getStringWidth } from "@visx/text";

// Basic width measurement
const width = getStringWidth("Hello world");
console.log(width); // e.g., 77.5

// Width with custom styling
const styledWidth = getStringWidth("Bold text", {
  fontFamily: 'Arial',
  fontSize: '16px',
  fontWeight: 'bold'
});
console.log(styledWidth); // e.g., 89.2

// Handle potential measurement failures
const safeWidth = getStringWidth("Some text") || 0;

Types

Comparison Function Type

/**
 * Generic type for comparison functions used in memoization
 * @param prev - Previous value (may be undefined)
 * @param next - Next value to compare
 * @returns Boolean indicating if values are equal
 */
type compareFunction<T> = (prev: T | undefined, next: T) => boolean;

Platform Requirements

  • Browser Environment: Requires DOM access for text measurement via document
  • React: Version 16.3.0 or higher
  • SVG Support: Requires SVG rendering capabilities

Error Handling

  • getStringWidth: Returns null if measurement fails (e.g., in non-browser environments)
  • Text Component: Safely handles invalid x/y coordinates by not rendering text
  • useText: Returns empty wordsByLines array for invalid positioning values

Implementation Details

Text Processing

  • Word Splitting: Text is split using the regex /(?:(?!\u00A0+)\s+)/ which preserves non-breaking spaces (\u00A0) while splitting on other whitespace
  • Space Width Calculation: Uses non-breaking space (\u00A0) for space width measurements to ensure consistent spacing
  • Line Breaking: Words are never split - line breaks only occur between complete words
  • Width Validation: X and Y coordinates must be finite numbers or valid strings (for percentage values)

Transform Generation

  • Scale-to-fit: When enabled, creates a matrix transform using the first line's width as reference
  • Rotation: Applied around the specified x,y coordinates as the rotation center
  • Transform Order: Scaling is applied before rotation in the transform chain

SVG Container

  • Container Element: Text is wrapped in an SVG element with overflow: 'visible' style
  • Positioning: Container uses dx/dy for offset positioning from the base x/y coordinates

Performance Considerations

  • Text Measurement: getStringWidth is memoized using lodash.memoize with string and style as cache keys
  • Layout Calculations: useText uses React.useMemo to prevent unnecessary recalculations
  • DOM Elements: Hidden SVG measurement element is reused across all getStringWidth calls
  • Conditional Processing: Layout calculations only run when width or scaleToFit props are provided