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 }}
/>
);
}
|