Skip to content

Commit 4f88f5f

Browse files
committed
refactor: Replace dangerouslySetInnerHTML with html-react-parser for safer content rendering across various components and pages.
Signed-off-by: Jessie Ssebuliba <jessiessebuliba@gmail.com>
1 parent 9e7e0b6 commit 4f88f5f

File tree

11 files changed

+149
-46
lines changed

11 files changed

+149
-46
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"dependencies": {
1616
"date-fns": "^4.1.0",
1717
"gray-matter": "^4.0.3",
18+
"html-react-parser": "^5.2.17",
1819
"next": "16.1.6",
1920
"react": "19.2.3",
2021
"react-dom": "19.2.3",

pnpm-lock.yaml

Lines changed: 107 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/blog/[slug]/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { format } from "date-fns";
44
import Link from "next/link";
55
import ShareButtons from "@/components/ShareButtons";
66
import { notFound } from "next/navigation";
7+
import parse from "html-react-parser";
78

89
export async function generateStaticParams() {
910
const posts = getAllPosts();
@@ -60,10 +61,9 @@ export default async function BlogPostPage({
6061
{/* Content */}
6162
<div className="container py-14 sm:py-[80px] lg:py-[90px]">
6263
<main className="w-full min-w-0 max-w-[800px] mx-auto">
63-
<div
64-
className="content text-sm text-charcoal font-normal sm:text-base"
65-
dangerouslySetInnerHTML={{ __html: post.contentHtml }}
66-
/>
64+
<div className="content text-sm text-charcoal font-normal sm:text-base">
65+
{parse(post.contentHtml)}
66+
</div>
6767
<div className="mt-11 mx-auto w-fit">
6868
<ShareButtons />
6969
</div>

src/app/hacktoberfest/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Metadata } from "next";
22
import { getSimplePage } from "@/lib/posts";
33
import IssueList from "@/components/IssueList";
4+
import parse from "html-react-parser";
45

56
export const metadata: Metadata = {
67
title: "Hacktoberfest 2024",
@@ -30,10 +31,9 @@ export default async function HacktoberfestPage() {
3031
</div>
3132
<div className="container py-14 sm:py-[80px] lg:py-[90px]">
3233
<main className="w-full min-w-0 max-w-[800px] mx-auto">
33-
<div
34-
className="content text-sm text-charcoal font-normal sm:text-base"
35-
dangerouslySetInnerHTML={{ __html: contentHtml }}
36-
/>
34+
<div className="content text-sm text-charcoal font-normal sm:text-base">
35+
{parse(contentHtml)}
36+
</div>
3737
<IssueList endpoint="https://hedera-issues.koyeb.app/api/hacktoberfest-issues" />
3838
</main>
3939
</div>

src/app/heroes/page.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Metadata } from "next";
22
import { getSimplePage } from "@/lib/posts";
33
import ContributorsGrid from "@/components/ContributorsGrid";
4+
import parse from "html-react-parser";
45

56
export const metadata: Metadata = {
67
title: "Hiero Heroes",
@@ -31,10 +32,9 @@ export default async function HeroesPage() {
3132
</div>
3233
<div className="container py-14 sm:py-[80px] lg:py-[90px]">
3334
<main className="w-full min-w-0 max-w-[800px] mx-auto">
34-
<div
35-
className="content text-sm text-charcoal font-normal sm:text-base"
36-
dangerouslySetInnerHTML={{ __html: contentHtml }}
37-
/>
35+
<div className="content text-sm text-charcoal font-normal sm:text-base">
36+
{parse(contentHtml)}
37+
</div>
3838
<ContributorsGrid endpoint="https://hedera-issues.koyeb.app/api/v2/contributors" />
3939
</main>
4040
</div>

src/app/layout.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ export default function RootLayout({
4040
return (
4141
<html lang="en">
4242
<body
43-
className={`${spaceGrotesk.className} ${spaceGrotesk.variable} ${ibmPlexMono.variable}`}
44-
>
43+
className={`${spaceGrotesk.className} ${spaceGrotesk.variable} ${ibmPlexMono.variable}`}>
4544
<header className="relative h-22.5">
4645
<Header />
4746
</header>

src/components/MeetSection.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import parse from "html-react-parser";
2+
13
type MeetCall = {
24
name: string;
35
description: string;
@@ -24,10 +26,9 @@ export default function MeetSection({ data }: MeetSectionProps) {
2426
<h2 className="text-2xl mb-2.5 sm:text-4xl sm:mb-5">
2527
{data.heading}
2628
</h2>
27-
<div
28-
className="text-lg max-w-full md:max-w-[800px] space-y-4"
29-
dangerouslySetInnerHTML={{ __html: data.text }}
30-
/>
29+
<div className="text-lg max-w-full md:max-w-[800px] space-y-4">
30+
{parse(data.text)}
31+
</div>
3132
</div>
3233
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8">
3334
{data.calls.map((call, i) => (

src/components/OpenSourceSection.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Image from "next/image";
2+
import parse from "html-react-parser";
23

34
type OpenSourceData = {
45
whyHeading: string;
@@ -46,10 +47,7 @@ export default function OpenSourceSection({ data }: OpenSourceSectionProps) {
4647
/>
4748
</div>
4849
<h2 className="text-2xl font-medium mb-5">{data.whatHeading}</h2>
49-
<p
50-
className="text-base max-w-[565px]"
51-
dangerouslySetInnerHTML={{ __html: data.whatText }}
52-
/>
50+
<div className="text-base max-w-[565px]">{parse(data.whatText)}</div>
5351
</div>
5452
</div>
5553
</div>

src/components/QuotesCarousel.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Swiper, SwiperSlide } from "swiper/react";
66
import { Autoplay } from "swiper/modules";
77
import type { SwiperRef } from "swiper/react";
88
import "swiper/css";
9+
import parse from "html-react-parser";
910

1011
type QuoteItem = {
1112
quote: string;
@@ -54,14 +55,12 @@ export default function QuotesCarousel({ data }: QuotesCarouselProps) {
5455
loading="lazy"
5556
/>
5657
</div>
57-
<div
58-
className="mb-5 [&>a]:text-white"
59-
dangerouslySetInnerHTML={{ __html: quote.quote }}
60-
/>
61-
<div
62-
className="font-bold mb-[155px] [&>a]:text-white"
63-
dangerouslySetInnerHTML={{ __html: quote.author }}
64-
/>
58+
<div className="mb-5 [&>a]:text-white">
59+
{parse(quote.quote)}
60+
</div>
61+
<div className="font-bold mb-[155px] [&>a]:text-white">
62+
{parse(quote.author)}
63+
</div>
6564
</SwiperSlide>
6665
))}
6766
</Swiper>

src/components/TSCSection.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState } from "react";
44
import Image from "next/image";
55
import tscMembers from "@/data/technical_steering_committee.json";
6+
import parse from "html-react-parser";
67

78
type Member = {
89
firstName: string;
@@ -34,10 +35,9 @@ export default function TSCSection() {
3435
<h2 className="text-3xl mb-2.5 sm:text-4xl sm:mb-4">
3536
{tscHeading}
3637
</h2>
37-
<div
38-
className="space-y-4 text-base sm:text-lg"
39-
dangerouslySetInnerHTML={{ __html: tscText }}
40-
/>
38+
<div className="space-y-4 text-base sm:text-lg">
39+
{parse(tscText)}
40+
</div>
4141
</div>
4242
</div>
4343
<div className="team-members grid grid-cols-1 sm:grid-cols-2 gap-x-10 gap-y-[40px] sm:gap-y-[60px]">
@@ -103,10 +103,9 @@ export default function TSCSection() {
103103
loading="lazy"
104104
/>
105105
</div>
106-
<div
107-
className="text-white mt-2 [&>p]:mb-4 [&>p:last-child]:mb-0"
108-
dangerouslySetInnerHTML={{ __html: member.bio }}
109-
/>
106+
<div className="text-white mt-2 [&>p]:mb-4 [&>p:last-child]:mb-0">
107+
{parse(member.bio)}
108+
</div>
110109
<button
111110
className="text-black text-lg rounded-full py-1.5 px-[26px] bg-white-dark self-center mt-4 cursor-pointer"
112111
onClick={() => setOpenBio(null)}>

0 commit comments

Comments
 (0)