diff --git a/package.json b/package.json
index ea011e26..88bdc983 100644
--- a/package.json
+++ b/package.json
@@ -98,6 +98,7 @@
     "sha256": "^0.2.0",
     "siwe": "^2.1.4",
     "slick-carousel": "^1.8.1",
+    "striptags": "3.2.0",
     "survey-analytics": "^1.9.109",
     "survey-core": "^1.9.90",
     "survey-creator-react": "^1.9.90",
diff --git a/src/app/quiz/List.js b/src/app/quiz/List.js
index b9dcfbdf..d1a378ab 100644
--- a/src/app/quiz/List.js
+++ b/src/app/quiz/List.js
@@ -30,10 +30,12 @@ import { fetchTeamList } from '#/domain/quiz/repository';
 import { ReactSelect } from '@/components/Select/ReactSelect';
 import { SearchIcon } from '@/components/Icons';
 import Input from '@/components/Input';
+import { markdownToPlainText } from '@/utils/markdown';
 import useMounted from '@/hooks/useMounted';
 
 function List({ data }) {
   const mediaUrl = useMediaUrl();
+
   return (
     <Link href={`/quiz/${data.id}`} className="p-6 bg-white flex max-md:flex-col gap-4 md:gap-9 mb-4 rounded-xl transition-all hover:shadow-[0_4px_24px_rgba(0,0,0,0.08)]">
       <div className="relative">
@@ -43,7 +45,7 @@ function List({ data }) {
       <div className="flex flex-col justify-between flex-1">
         <div>
           <h3 className="text-2xl mb-2">{data?.title}</h3>
-          <p className="text-base md:mb-2 opacity-60 md:line-clamp-2">{data?.describe}</p>
+          <p className="text-base md:mb-2 opacity-60 md:line-clamp-2">{markdownToPlainText(data?.describe)}</p>
           {data?.reward_text && <div className="flex w-fit pr-2 items-center h-6 bg-[rgba(239,78,22,0.1)] rounded-full max-md:mt-4">
             <div className="w-6 h-6 rounded-full flex items-center justify-center bg-[#EF4E16] mr-2">
               <Image width={16} height={16} src={TrophiesSvg} alt="Trophies" />
diff --git a/src/app/quiz/[id]/RankList.js b/src/app/quiz/[id]/RankList.js
new file mode 100644
index 00000000..307c7c99
--- /dev/null
+++ b/src/app/quiz/[id]/RankList.js
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2024 OpenBuild
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use client';
+
+import Image from 'next/image';
+import Rank1Icon from 'public/images/svg/rank-1.svg';
+import Rank2Icon from 'public/images/svg/rank-2.svg';
+import Rank3Icon from 'public/images/svg/rank-3.svg';
+import { useMediaUrl } from '#/state/application/hooks';
+
+export default function RankList({ rank, list }) {
+  const mediaUrl = useMediaUrl();
+
+  return (
+    <div className="flex flex-col h-full">
+      <div className="border border-gray-600 rounded flex flex-col h-full overflow-hidden">
+        <h6 className="h-12 bg-gray-1000 text-center leading-[48px] relative flex-shrink-0">
+          Quiz Scoreboard
+          {rank > 0 && (
+            <p className="absolute right-6 top-[14px] text-sm font-normal">
+              <span className="opacity-60">My ranking: </span>
+              {rank}
+            </p>
+          )}
+        </h6>
+        <ul className="p-4 overflow-y-auto flex-grow">
+          {list?.map((i, k) => (
+            <li key={`QuizScoreboard-${k}`} className="flex items-center justify-between mb-4 last:mb-0">
+              <div className="flex items-center">
+                {k === 0 && <Image alt="" src={Rank1Icon} className="mr-2 w-5" />}
+                {k === 1 && <Image alt="" src={Rank2Icon} className="mr-2 w-5" />}
+                {k === 2 && <Image alt="" src={Rank3Icon} className="mr-2 w-5" />}
+                {k > 2 && <span className="inline-block w-5 text-center mr-2 text-xs opacity-40">{k + 1}</span>}
+                {mediaUrl && i?.user?.user_avatar ? (
+                  <Image
+                    className="h-6 w-6 rounded object-cover mr-2"
+                    height={24}
+                    width={24}
+                    alt={'user_avatar'}
+                    src={`${mediaUrl}${i.user.user_avatar}`}
+                  />
+                ) : (
+                  <span className="h-6 w-6 rounded-full bg-gray-200 mr-2 flex items-center justify-center text-xs">
+                    {i?.user?.user_nick_name?.[0]?.toUpperCase() || 'U'}
+                  </span>
+                )}
+                <p className="text-[12px] max-md:leading-[20px] md:text-sm">
+                  <a href={`/u/${i?.user?.user_handle}`}>{i?.user?.user_nick_name}</a>
+                </p>
+              </div>
+              <p className="max-md:text-[12px] max-md:leading-[24px]">{i?.score}</p>
+            </li>
+          ))}
+        </ul>
+      </div>
+    </div>
+  );
+}
diff --git a/src/app/quiz/[id]/RankListModal.js b/src/app/quiz/[id]/RankListModal.js
new file mode 100644
index 00000000..b3126c28
--- /dev/null
+++ b/src/app/quiz/[id]/RankListModal.js
@@ -0,0 +1,52 @@
+/**
+ * Copyright 2024 OpenBuild
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Modal } from '@/components/Modal';
+import RankList from './RankList';
+import { useState } from 'react';
+import { fetchRankList } from '#/domain/quiz/repository';
+import Loader from '@/components/Loader';
+import { ModalCloseIcon } from '@/components/Icons';
+import useMounted from '@/hooks/useMounted';
+
+export function RankListModal({ quizId, shown, onClose, rank }) {
+  const [data, setData] = useState(null);
+  const [loading, setLoading] = useState(true);
+
+  useMounted(() => {
+    setLoading(true);
+    fetchRankList({ quizId })
+      .then(res => {
+        setData(res?.data?.list?.rank);
+      })
+      .finally(() => {
+        setLoading(false);
+      });
+  });
+
+  return (
+    <Modal isOpen={shown} closeModal={onClose} container mode="640">
+      <ModalCloseIcon
+        onClick={onClose}
+        className="absolute top-[-48px] md:top-[-32px] right-0 md:right-[-32px] cursor-pointer"
+      />
+      <div className="md:h-[600px] h-[400px] flex flex-col overflow-y-auto rounded-inherit overflow-hidden">
+        {loading && <Loader classname="mr-2" />}
+        <RankList rank={rank} list={data} />
+      </div>
+    </Modal>
+  );
+}
diff --git a/src/app/quiz/[id]/Record.js b/src/app/quiz/[id]/Record.js
index 5e4725a4..b10ce5cd 100644
--- a/src/app/quiz/[id]/Record.js
+++ b/src/app/quiz/[id]/Record.js
@@ -25,14 +25,14 @@ import { formatTime, fromUtcOffset, formatTimeMeridiem } from '@/utils/date';
 import { NoData } from '@/components/NoData';
 import Link from 'next/link';
 
-export function Record({id, openModal, closeModal}) {
+export function Record({quizId, shown, onClose}) {
   const mediaUrl = useMediaUrl();
-  const { data } = useSWR(openModal ? `/ts/v1/quiz/${id}/answer` : null, fetcher);
+  const { data } = useSWR(shown ? `/ts/v1/quiz/${quizId}/answer` : null, fetcher);
 
   return (
-    <Modal isOpen={openModal} closeModal={closeModal} container mode="640">
+    <Modal isOpen={shown} onClose={onClose} container mode="640">
       <div >
-        <ModalCloseIcon onClick={closeModal} className="absolute top-[-48px] md:top-[-32px] right-0 md:right-[-32px] cursor-pointer" />
+        <ModalCloseIcon onClick={onClose} className="absolute top-[-48px] md:top-[-32px] right-0 md:right-[-32px] cursor-pointer" />
         <div>
           <h3 className="text-center py-4 border-b border-gray-600">
             Challenge Record
@@ -85,7 +85,7 @@ export function Record({id, openModal, closeModal}) {
               ))}
             </div>
           </div>
-          {data?.length === 0 && <div className="pb-12"><NoData /></div>}
+          {(!data|| data?.length === 0) && <div className="pb-12"><NoData /></div>}
         </div>
       </div>
     </Modal>
diff --git a/src/app/quiz/[id]/page.js b/src/app/quiz/[id]/page.js
index 9a8bbbaa..7d1d5013 100644
--- a/src/app/quiz/[id]/page.js
+++ b/src/app/quiz/[id]/page.js
@@ -22,9 +22,6 @@ import { useMediaUrl } from '#/state/application/hooks';
 import { ArrowUturnLeftIcon } from '@heroicons/react/24/solid';
 import { HistoryIcon } from '@/components/Icons';
 import { Button } from '@/components/Button';
-import Rank1Icon from 'public/images/svg/rank-1.svg';
-import Rank2Icon from 'public/images/svg/rank-2.svg';
-import Rank3Icon from 'public/images/svg/rank-3.svg';
 import { useState } from 'react';
 import useSWR from 'swr';
 import { fetcher } from '@/utils/request';
@@ -33,10 +30,14 @@ import { useSession } from 'next-auth/react';
 import { useRouter } from 'next/navigation';
 import { Record } from './Record';
 import QuizLimiterWidget from '#/domain/quiz/widgets/quiz-limiter';
+import RankList from './RankList';
+import { RankListModal } from './RankListModal';
+import { OViewer } from '@/components/MarkDown';
 
 export default function Quiz({params}) {
   const mediaUrl = useMediaUrl();
-  const [openModal, setOpenModal] = useState(false);
+  const [openChallenge, setOpenChallenge] = useState(false);
+  const [openRankList, setOpenRankList] = useState(false);
   const [checkLimit, setCheckLimit] = useState(false);
   const { data } = useSWR(`/ts/v1/quiz/${params.id}/index`, fetcher);
   const { data: coursesList } = useSWR(`v1/learn/course/opencourse?skip=0&take=2&order=default&quiz_bind_id=${params.id}`, fetcher);
@@ -62,7 +63,7 @@ export default function Quiz({params}) {
             if (status !== 'authenticated') {
               router.push(`/signin?from=/quiz/${params.id}`);
             } else {
-              setOpenModal(true);
+              setOpenChallenge(true);
             }
           }} className="cursor-pointer transition-all flex text-sm items-center opacity-80 rounded py-2 px-3 border border-gray-1100 text-black hover:border-gray">
             <HistoryIcon className="mr-2" />Challenge Record
@@ -84,55 +85,26 @@ export default function Quiz({params}) {
       </div>
       <div className="max-w-[800px] mx-auto bg-white rounded-xl p-6 md:px-9 md:pt-10 md:pb-6  relative z-[2] md:top-[-155px]">
         <h5 className="text-lg mb-4 md:mb-3">Quiz Describe</h5>
-        <p dangerouslySetInnerHTML={{__html: data?.describe.replace('\n', '<br>')}}>
-          {/* {data?.describe} */}
-        </p>
+        <OViewer value={data?.describe} />
         <Button
           onClick={() => setCheckLimit(true)}
           className="mt-4 md:mt-6 mb-9 md:mb-10 !font-bold px-[64px] !text-base max-md:w-full">
             Challenge now
         </Button>
-        <div>
-          <div className="border border-gray-600 rounded">
-            <h6 className="h-12 bg-gray-1000 text-center leading-[48px] rounded-t relative">
-              Quiz Scoreboard
-              {data?.my_rank > 0 && <p className="absolute right-6 top-[14px] text-sm font-normal">
-                <span className="opacity-60">My ranking: </span>
-                {data?.my_rank}
-              </p>}
-            </h6>
-            <ul className="p-4">
-              {data?.rank?.map((i, k) => (
-                <li key={`QuizScoreboard-${k}`} className="flex items-center justify-between mb-4 last:mb-0">
-                  <div className="flex items-center">
-                    {k === 0 && <Image alt="" src={Rank1Icon} className="mr-2 w-5" />}
-                    {k === 1 && <Image alt="" src={Rank2Icon} className="mr-2 w-5" />}
-                    {k === 2 && <Image alt="" src={Rank3Icon} className="mr-2 w-5" />}
-                    {k > 2 && <span className="inline-block w-5 text-center mr-2 text-xs opacity-40">{k + 1}</span>}
-                    <Image
-                      className="h-6 w-6 rounded object-cover mr-2"
-                      height={24}
-                      width={24}
-                      alt={'user_avatar'}
-                      src={mediaUrl + i?.user?.user_avatar}
-                    />
-                    <p className="text-[12px] max-md:leading-[20px] md:text-sm"><a href={`/u/${i?.user?.user_handle}`}>{i?.user?.user_nick_name}</a></p>
-                  </div>
-                  <p className="max-md:text-[12px] max-md:leading-[24px]">{i.score}</p>
-                </li>
-              ))}
-            </ul>
-          </div>
-          <p className="text-sm text-center mt-6"><strong>{data?.user_num}</strong> builders have participated</p>
-        </div>
-      </div>
-      <div className="max-w-[800px] max-md:mt-9 mx-6 md:mx-auto relative md:top-[-105px] max-md:pb-14">
-        <h3 className="text-[18px] max-md:leading-[24px] md:text-lg mb-6">Related courses</h3>
-        <div className="grid gap-y-6 md:gap-4 md:grid-cols-2">
-          {coursesList?.list?.map(i => <CourseCard data={i} key={`open-courses-${i.base.course_series_id}`} />)}
-        </div>
+        <RankList rank={data?.my_rank} list={data?.rank}/>
+        <p className="text-sm text-center mt-6 cursor-pointer" onClick={()=>{setOpenRankList(true)}}><strong>{data?.user_num}</strong> builders have participated</p>
       </div>
-      <Record id={params.id} openModal={openModal} closeModal={() => setOpenModal(false)} />
+      {
+        coursesList?.count > 0 && (
+          <div className="max-w-[800px] max-md:mt-9 mx-6 md:mx-auto relative md:top-[-105px] max-md:pb-14">
+            <h3 className="text-[18px] max-md:leading-[24px] md:text-lg mb-6">Related courses</h3>
+            <div className="grid gap-y-6 md:gap-4 md:grid-cols-2">
+              {coursesList?.list?.map(i => <CourseCard data={i} key={`open-courses-${i.base.course_series_id}`} />)}
+            </div>
+          </div>)
+      }
+      <RankListModal quizId={params.id} shown={openRankList} onClose={() => setOpenRankList(false)}  rank={data?.my_rank}/>
+      <Record quizId={params.id} shown={openChallenge} onClose={() => setOpenChallenge(false)} />
     </QuizLimiterWidget>
   );
 }
diff --git a/src/domain/quiz/repository.js b/src/domain/quiz/repository.js
index 0680ddc7..1ceea85b 100644
--- a/src/domain/quiz/repository.js
+++ b/src/domain/quiz/repository.js
@@ -49,4 +49,8 @@ async function fetchTeamList(){
   return httpClient.get('/quiz/team');
 }
 
-export { updateRespondentContacts, fetchPublishedQuizList, fetchAnsweredQuizList, fetchAnsweredResult, fetchTeamList };
+async function fetchRankList({ quizId }){
+  return httpClient.get(`/quiz/${quizId}/users`);
+}
+
+export { updateRespondentContacts, fetchPublishedQuizList, fetchAnsweredQuizList, fetchAnsweredResult, fetchTeamList, fetchRankList };
diff --git a/src/shared/utils/markdown.js b/src/shared/utils/markdown.js
index 604af0be..89d2a332 100644
--- a/src/shared/utils/markdown.js
+++ b/src/shared/utils/markdown.js
@@ -22,6 +22,7 @@ import breaks from '@bytemd/plugin-breaks';
 import math from '@bytemd/plugin-math';
 import mermaid from '@bytemd/plugin-mermaid';
 import gemoji from '@bytemd/plugin-gemoji';
+import striptags from 'striptags';
 
 const plugins = [gfm(), breaks(), highlight(), math(), mermaid(), gemoji()];
 
@@ -53,4 +54,11 @@ function renderHtml(markdownContent) {
   return getProcessor({ sanitize, plugins }).processSync(markdownContent).toString();
 }
 
-export { getPlugins, sanitize, renderMarkdown, renderHtml };
+function markdownToPlainText(markdownContent) {
+  const html = renderHtml(markdownContent);
+  const plainText = striptags(html);
+  
+  return plainText.trim().replace(/[\r\n]+/g, ' ');
+}
+
+export { getPlugins, sanitize, renderMarkdown, renderHtml, markdownToPlainText };