I know two approaches. One is free-form rendering, like Anthropic's Artifacts. The other is to designate a category of tools whose output gets caught by the frontend and rendered in the chat using the corresponding components. I used the second one in several projects.