Skip to content

Instantly share code, notes, and snippets.

@kerns
Created November 20, 2024 10:11
Show Gist options
  • Save kerns/2e0feab1f8538b9e496cd9c52ef0bf55 to your computer and use it in GitHub Desktop.
Save kerns/2e0feab1f8538b9e496cd9c52ef0bf55 to your computer and use it in GitHub Desktop.
A React / Next.js 15 Friendly unicorn.studio Embed
'use client';
import { useEffect, useRef, useState } from 'react';
export type UnicornSceneProps = {
projectId: string;
width?: number | string;
height?: number | string;
scale?: number;
dpi?: number;
altText?: string;
ariaLabel?: string;
className?: string;
isEditingOrPreviewing?: boolean;
};
export default function UnicornScene({
projectId,
width = "100%",
height = "100%",
scale = 0.85,
dpi = 2,
altText = "Unicorn Studio Animation",
ariaLabel = altText,
className = "",
isEditingOrPreviewing = false,
}: UnicornSceneProps) {
const elementRef = useRef<HTMLDivElement>(null);
const [error, setError] = useState<string | null>(null);
const [scriptLoaded, setScriptLoaded] = useState(false);
// Load the UnicornStudio script
useEffect(() => {
if (typeof window === 'undefined') return;
const script = document.createElement('script');
script.src = 'https://cdn.unicorn.studio/v1.3.2/unicornStudio.umd.js';
script.async = true;
script.onload = () => setScriptLoaded(true);
script.onerror = () => setError('Failed to load UnicornStudio script');
document.body.appendChild(script);
return () => {
script.parentNode?.removeChild(script);
};
}, []);
// Initialize the scene
useEffect(() => {
if (!scriptLoaded || !elementRef.current) return;
// Add cache-busting for preview mode
const cleanProjectId = projectId.split("?")[0];
const cacheBuster = isEditingOrPreviewing ? `?update=${Math.random()}` : '';
// Set the project ID attribute
elementRef.current.setAttribute('data-us-project', cleanProjectId + cacheBuster);
// Initialize UnicornStudio
const initUnicorn = async () => {
try {
const UnicornStudio = (window as any).UnicornStudio;
if (!UnicornStudio) {
throw new Error('UnicornStudio not found');
}
if (!UnicornStudio.isInitialized) {
// Initialize with configuration options directly
await UnicornStudio.init({
scale,
dpi,
});
UnicornStudio.isInitialized = true;
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to initialize UnicornStudio');
}
};
initUnicorn();
return () => {
const UnicornStudio = (window as any).UnicornStudio;
if (UnicornStudio?.destroy) {
UnicornStudio.destroy();
}
};
}, [scriptLoaded, projectId, scale, dpi, isEditingOrPreviewing]);
return (
<div
ref={elementRef}
style={{
width: typeof width === 'number' ? `${width}px` : width,
height: typeof height === 'number' ? `${height}px` : height
}}
className={`relative ${className}`}
role="img"
aria-label={ariaLabel}
>
{error && <div className="text-red-500">{error}</div>}
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment