On reload, canvas flashes as a blank square (cobe globe)
Unanswered
Orinoco Crocodile posted this in #help-forum
Orinoco CrocodileOP
Hello, I use https://cobe.vercel.app/docs in my nextjs typescript app. it works fine, except when I do a page reload, the canvas use by the globe become a white square for a brief moment at the instant you hit refresh, then comes back to normal. Do you know how to solve that ? it happens in both
I use it inside a component, that is then imported in my
I use the latest nextjs stable version, same for all the other stuff I use.
I also tried to put the globe code directly inside my home.tsx to see if it was because it was inside a component, but I had the same bug.
Thank you.
pnpm run dev, and pnpm run build then pnpm run start, the only difference is that its less consistent in pnpm run dev, it only flashes 50% of the time.I use it inside a component, that is then imported in my
home.tsxI use the latest nextjs stable version, same for all the other stuff I use.
// src/components/home/CTA.tsx
import { getTranslations } from 'next-intl/server';
import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import dynamic from 'next/dynamic';
import Image from "next/image";
const Globe = dynamic(() => import('@/components/home/Globe').then(mod => mod.Globe), { ssr: false });
export async function CTA() {
const t = await getTranslations('home');
return (
<div className="h-full flex flex-col justify-between">
<Card className="mb-4 relative overflow-hidden">
<div className="absolute inset-0 bg-card z-10" />
<div className="absolute inset-0 z-20">
<div className="absolute bottom-[-10%] right-[-30%] sm:bottom-[-30%] sm:right-[-30%] w-[120%] h-[120%] sm:w-[140%] sm:h-[140%] flex items-end justify-end overflow-hidden">
<div className="relative w-[300px] h-[300px] sm:w-[380px] sm:h-[380px]">
<Globe width={380} height={380} />
</div>
</div>
</div>
<CardContent className="pt-6 relative z-30">
<p className="text-base text-muted-foreground mb-2">{t("cta2Subtitle")}</p>
<h3 className="text-3xl font-semibold text-foreground mb-2">
{t("cta2Title")}
</h3>
<p className="text-base text-muted-foreground mb-8 max-w-[60%]">
{t("cta2Description")}
</p>
<Button>{t("ctaButton2")}</Button>
</CardContent>
</Card>
...// src/components/home/Globe.tsx
'use client';
import { useEffect, useRef } from 'react';
import createGlobe from 'cobe';
interface GlobeProps {
width: number;
height: number;
}
export const Globe: React.FC<GlobeProps> = ({ width, height }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
let phi = 3.1;
const canvasElement = canvasRef.current;
if (!canvasElement) return;
const globe = createGlobe(canvasElement, {
devicePixelRatio: 2,
width: width * 2,
height: height * 2,
phi: 0,
theta: 0,
dark: 1,
diffuse: 1.2,
mapSamples: 16000,
mapBrightness: 6,
baseColor: [0.3, 0.3, 0.3],
markerColor: [0.369, 0.361, 0.992],
glowColor: [0.369, 0.361, 0.992],
markers: [
// Paris
{ location: [48.8566, 2.3522], size: 0.05 },
// Add more markers as needed
{ location: [40.7128, -74.0060], size: 0.03 }, // New York
{ location: [35.6762, 139.6503], size: 0.03 }, // Tokyo
],
onRender: (state) => {
state.phi = phi;
phi += 0.003;
}
});
return () => {
globe.destroy();
};
}, [width, height]);
return (
<canvas
ref={canvasRef}
style={{ width: width, height: height, maxWidth: "100%", aspectRatio: 1 }}
/>
);
};I also tried to put the globe code directly inside my home.tsx to see if it was because it was inside a component, but I had the same bug.
Thank you.

5 Replies
Orinoco CrocodileOP
To give more details after some tests :
The white square appears at the exact instant you hit refresh. and disappear once the loading is finished. The bug never happens on page change. the globe is on my home. If im on any page, and I go back to my home, the white square will never appear. It only appears when I'm already on the homepage, and I hit refresh. its the only case when it appears.
The white square appears at the exact instant you hit refresh. and disappear once the loading is finished. The bug never happens on page change. the globe is on my home. If im on any page, and I go back to my home, the white square will never appear. It only appears when I'm already on the homepage, and I hit refresh. its the only case when it appears.
@Orinoco Crocodile To give more details after some tests :
The white square appears at the exact instant you hit refresh. and disappear once the loading is finished. The bug never happens on page change. the globe is on my home. If im on any page, and I go back to my home, the white square will never appear. It only appears when I'm already on the homepage, and I hit refresh. its the only case when it appears.
I'm having this issue too. Did you find a solution?
@rrrairakku I'm having this issue too. Did you find a solution?
Orinoco CrocodileOP
had to do some workarounds, not really optimised :
And in my component where its used :
src/components/home/Globe.tsx'use client';
import type React from 'react';
import { useEffect, useRef, useState } from 'react';
import createGlobe from 'cobe';
import { SimpleGlobe } from './SimpleGlobe';
interface GlobeProps {
width: number;
height: number;
}
export const Globe: React.FC<GlobeProps> = ({ width, height }) => {
const canvasRef = useRef<HTMLCanvasElement>(null);
const overlayRef = useRef<HTMLDivElement>(null);
const [isCobeReady, setIsCobeReady] = useState(false);
useEffect(() => {
let phi = 3.1;
let globe: ReturnType<typeof createGlobe> | null = null;
const initCobe = () => {
const canvasElement = canvasRef.current;
if (!canvasElement) return;
globe = createGlobe(canvasElement, {
devicePixelRatio: 2,
width: width * 2,
height: height * 2,
phi: 0,
theta: 0,
dark: 1,
diffuse: 1.2,
mapSamples: 16000,
mapBrightness: 6,
baseColor: [0.3, 0.3, 0.3],
markerColor: [0.369, 0.361, 0.992],
glowColor: [0.369, 0.361, 0.992],
markers: [
{ location: [48.8566, 2.3522], size: 0.05 },
{ location: [40.7128, -74.0060], size: 0.03 },
{ location: [35.6762, 139.6503], size: 0.03 },
],
onRender: (state) => {
state.phi = phi;
phi += 0.003;
}
});
setIsCobeReady(true);
};
initCobe();
return () => {
globe?.destroy();
};
}, [width, height]);
useEffect(() => {
const handleBeforeUnload = () => {
if (overlayRef.current) {
overlayRef.current.style.opacity = '1';
}
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, []);
return (
<div style={{ position: 'relative', width, height }}>
<div
ref={overlayRef}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: 10,
opacity: 0,
}}
>
<SimpleGlobe width={width} height={height} />
</div>
{!isCobeReady && <SimpleGlobe width={width} height={height} />}
<canvas
ref={canvasRef}
style={{
width: width,
height: height,
maxWidth: "100%",
aspectRatio: 1,
position: 'absolute',
top: 0,
left: 0,
opacity: isCobeReady ? 1 : 0,
}}
/>
</div>
);
};And in my component where its used :
const Globe = dynamic(
() => import("@/components/home/Globe").then((mod) => mod.Globe),
{ ssr: false }
);---
the SimpleGlobe component is just a skeleton
the SimpleGlobe component is just a skeleton