How to interact with innerHTML

I'm trying to solve the following. I get sanitised HTML string from my Markdown renderer. I put use it in an innerHTML. I'd like to parse it, recognise the code blocks and add a "Copy to clipboard" button to each code block (top and bottom corners). This is the best code I could come up. Since I never used createRoot, getOwner or insert before, I'd like to ask if this is the correct usage or not. The alternative would have been to render, but I think createRoot is faster, right?
import { CopyButton } from '@shared/components/ui/CopyButton'
import { base64ToUtf8 } from '@shared/lib/utils'
import { type Component, createRoot, getOwner, onCleanup, onMount } from 'solid-js'
import { insert } from 'solid-js/web'

export const AssistantMessage: Component<{
message: string
}> = (props) => {
let contentRef!: HTMLDivElement
const disposers: Array<() => void> = []

const getHTML = () => {
return props.message
}

onMount(() => {
if (!contentRef) return

const owner = getOwner()

const codeBlocks = contentRef.querySelectorAll('.code-block-container')

codeBlocks.forEach((block) => {
const encoded = block.getAttribute('data-code')
if (!encoded) return

const code = base64ToUtf8(encoded)

const topContainer = document.createElement('div')
const bottomContainer = document.createElement('div')

block.prepend(topContainer)
block.append(bottomContainer)

createRoot((dispose) => {
insert(topContainer, () => <CopyButton code={code} position="top" />)
disposers.push(dispose)
}, owner)

createRoot((dispose) => {
insert(bottomContainer, () => <CopyButton code={code} position="bottom" />)
disposers.push(dispose)
}, owner)
})
})

onCleanup(() => {
disposers.forEach((d) => {
d()
})
})

return (
<div>
<div ref={contentRef} innerHTML={getHTML()} />
</div>
)
}
import { CopyButton } from '@shared/components/ui/CopyButton'
import { base64ToUtf8 } from '@shared/lib/utils'
import { type Component, createRoot, getOwner, onCleanup, onMount } from 'solid-js'
import { insert } from 'solid-js/web'

export const AssistantMessage: Component<{
message: string
}> = (props) => {
let contentRef!: HTMLDivElement
const disposers: Array<() => void> = []

const getHTML = () => {
return props.message
}

onMount(() => {
if (!contentRef) return

const owner = getOwner()

const codeBlocks = contentRef.querySelectorAll('.code-block-container')

codeBlocks.forEach((block) => {
const encoded = block.getAttribute('data-code')
if (!encoded) return

const code = base64ToUtf8(encoded)

const topContainer = document.createElement('div')
const bottomContainer = document.createElement('div')

block.prepend(topContainer)
block.append(bottomContainer)

createRoot((dispose) => {
insert(topContainer, () => <CopyButton code={code} position="top" />)
disposers.push(dispose)
}, owner)

createRoot((dispose) => {
insert(bottomContainer, () => <CopyButton code={code} position="bottom" />)
disposers.push(dispose)
}, owner)
})
})

onCleanup(() => {
disposers.forEach((d) => {
d()
})
})

return (
<div>
<div ref={contentRef} innerHTML={getHTML()} />
</div>
)
}
8 Replies
ladybluenotes
ladybluenotes2mo ago
Are you able to use remark/rehype plugins to parse it? Imo that might be an easier way to address it
hyperknot
hyperknotOP2mo ago
I'm using marked. It's already parsing it. but I don't want to recreate the full html renderer into JSX components
ladybluenotes
ladybluenotes2mo ago
Yeah, fair. Is this just a component within the app you're wanting to have the markdown in or will it be the whole of it? I believe your code looks familiar to what I did at work (and I'm sorry but I'm blanking on a good majority of it all because it took ages to figure out) but how you're approaching it looks familiar to what I did. @jer3m01 or @Brendonovich might have some better suggestions (they build SolidBase so they're regularly in with markdown and such)
hyperknot
hyperknotOP2mo ago
no, it's just one component and inside that component there are possibly <pre><code> block, for which I want to put clipboard icons
ladybluenotes
ladybluenotes2mo ago
That's stuff they might be able to help with, I didn't inject any code snippets in mine. It might be worthwhile to take a look here?? You can see how they approached it (albeit with a larger app but you might be able to adapt to what you need) https://github.com/kobaltedev/solidbase/tree/main/src/vite-mdx
GitHub
solidbase/src/vite-mdx at main · kobaltedev/solidbase
Fully featured, fully customisable static site generation for SolidStart - kobaltedev/solidbase
hyperknot
hyperknotOP2mo ago
thanks
Brendonovich
Brendonovich2mo ago
Our MDX stuff is more focused on compiling MDX to JSX at build time/on the server, using innerHtml like this seems fine for client-only work
hyperknot
hyperknotOP2mo ago
Thanks! I just wanted to know if I'm using createRoot, getOwner and insert correctly

Did you find this page helpful?