Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | 19x 19x 13x 35x 35x 24x 24x 24x 24x 24x 35x | import { PropsWithChildren, ReactElement, useEffect, useState } from "react"; import DOMPurify from "isomorphic-dompurify"; interface Props { className?: string; allowedTags?: string[]; allowedAttributes?: string[]; } export default function SafeHTML({ children, className = "", allowedTags = ["p", "br", "strong", "em", "a", "span"], allowedAttributes = ["href", "target", "rel"], }: PropsWithChildren<Props>): ReactElement | null { const [sanitizedContent, setSanitizedContent] = useState(""); useEffect(() => { const spotifyUrlRegex = /^(?:(?:(?:f|ht)tps?|spotify):|[^a-z]|[a-z+.-]+(?:[^a-z+.\-:]|$))/i; const htmlString = children?.toString() ?? ""; DOMPurify.addHook("afterSanitizeAttributes", (node) => { Iif (node instanceof Element && node.tagName === "A") { node.setAttribute("target", "_blank"); node.setAttribute("rel", "noopener noreferrer"); } }); const clean = DOMPurify.sanitize(htmlString, { ALLOWED_URI_REGEXP: spotifyUrlRegex, ALLOWED_TAGS: allowedTags, ALLOWED_ATTR: allowedAttributes, ADD_ATTR: ["target:_blank", "rel:noopener noreferrer"], }); setSanitizedContent(clean); }, [allowedAttributes, allowedTags, children]); if (!sanitizedContent) { return <span className={className} />; } return ( <span className={className} dangerouslySetInnerHTML={{ __html: sanitizedContent }} /> ); } |