Search code examples
htmlcsssvg

Make the width of <svg>grow to match the length of it's <text>


I have a react component where it shows a <svg>. There is a <text> attribute which will be changed over the time. I want make the <svg> grow it's width to display the complete text. How to make following component's size dynamic to the length of the text?

const S = {
    Container: styled.div`
        background: red;
        width: '10px';
        height: '10px';
    `
};

export class RectNode extends Component<IRectNodeProps> {
    render() {
        return (
            <S.Container>
                <svg width="auto" height="auto">
                    <rect
                        x="0"
                        y="0"
                        width="100%"
                        height="100%"
                        stroke="red"
                        stroke-width="3px"
                        fill="white"
                    />
                    // more shapes here
                    <text
                        x="50%"
                        y="50%"
                        dominant-baseline="middle"
                        text-anchor="middle"
                    >
                        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
                    </text>
                </svg>
            </S.Container>
        );
    }
}

JS Fiddle:

<svg width="auto" height="auto">
  <rect
        x="0"
        y="0"
        width="100%"
        height="100%"
        stroke="red"
        stroke-width="3px"
        fill="white"
        />
  // more shapes here
  <text
        x="50%"
        y="50%"
        dominant-baseline="middle"
        text-anchor="middle"
        >
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa
  </text>
</svg>


Solution

  • Get the text BBox after render and set the SVG width attribute by changing state. The example here.

    class SvgComponent extends React.Component {
        state = {width: 0};
    
        componentDidMount() {
            this.updateTextSize();
        }
    
        componentDidUpdate() {
            this.updateTextSize();
        }
    
        updateTextSize() {
            const box = this.text.getBBox();
            if(this.state.width !== box.width) {
                this.setState({width: box.width});
            }
        }
    
        render() {
            return (
            <svg width={this.state.width} height="40">
                <text ref={handle => this.text = handle} x="50%" y="25" textAnchor="middle">
                    {this.props.text}
                </text>
            </svg>
            );
        }
    }