diff --git a/components/Nft/NftContentRenderer.js b/components/Nft/NftContentRenderer.js
new file mode 100644
index 000000000..88ed06c0a
--- /dev/null
+++ b/components/Nft/NftContentRenderer.js
@@ -0,0 +1,189 @@
+import { useTranslation } from 'next-i18next'
+import Head from 'next/head'
+import { useRef, useEffect } from 'react'
+import ReactPannellum from 'react-pannellum'
+import LoadingGif from '../../public/images/loading.gif'
+import {
+ needNftAgeCheck,
+ nftName,
+ renderLoadingImage,
+ handle18PlusClick
+} from '../../utils/nft'
+
+export default function NftContentRenderer({
+ nft,
+ contentTab,
+ imageUrl,
+ videoUrl,
+ modelUrl,
+ defaultTab,
+ defaultUrl,
+ clUrl,
+ imageStyle,
+ modelAttr,
+ isPanoramic,
+ loaded,
+ errored,
+ setLoaded,
+ setErrored,
+ setShowAgeCheck,
+ classNamePrefix = 'fv-preview',
+ panoramaIds = { image: '1', video: '2' },
+ panoramaSceneIds = { image: 'firstScene', video: 'videoScene' }
+}) {
+ const { t } = useTranslation()
+ const modelViewerRef = useRef(null)
+
+ const clickOn18PlusImage = () => handle18PlusClick(setShowAgeCheck)
+
+ useEffect(() => {
+ const modelViewer = modelViewerRef.current
+ if (!modelViewer || defaultTab !== 'model') return
+
+ const handleLoad = () => {
+ setLoaded(true)
+ setErrored(false)
+ }
+
+ const handleError = () => {
+ setErrored(true)
+ }
+
+ modelViewer.addEventListener('load', handleLoad)
+ modelViewer.addEventListener('error', handleError)
+
+ return () => {
+ modelViewer.removeEventListener('load', handleLoad)
+ modelViewer.removeEventListener('error', handleError)
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [defaultTab, modelUrl])
+
+ return (
+ <>
+ {needNftAgeCheck(nft) ? (
+
+ ) : (
+ <>
+ {imageUrl && contentTab === 'image' && (
+ <>
+ {renderLoadingImage(errored, loaded, t)}
+ {isPanoramic ? (
+ {
+ if (classNamePrefix === 'fv-fullscreen') {
+ return {
+ imageRendering: imageStyle.imageRendering,
+ filter: imageStyle.filter,
+ display: loaded ? 'inline-block' : 'none'
+ }
+ }
+ return {
+ ...imageStyle,
+ display: loaded ? 'inline-block' : 'none'
+ }
+ })()}
+ src={imageUrl}
+ onLoad={() => {
+ setLoaded(true)
+ setErrored(false)
+ }}
+ onError={({ currentTarget }) => {
+ if (currentTarget.src === imageUrl && imageUrl !== clUrl.image) {
+ currentTarget.src = clUrl.image
+ } else {
+ setErrored(true)
+ }
+ }}
+ alt={nftName(nft)}
+ />
+ )}
+ >
+ )}
+
+ {videoUrl && defaultTab === 'video' && (
+ <>
+ {renderLoadingImage(errored, loaded, t)}
+ {isPanoramic ? (
+
- ) : (
- <>
- {imageUrl && contentTab === 'image' && (
- <>
- {loadingImage(nft)}
- {isPanoramic ? (
-