T
TanStack4w ago
deep-jade

Only render component on the client?

Hi all, wondering what is the best way when you want to render React component only on client side in TanStack Start? I'm trying something like
import { HelpCircle } from 'lucide-react';

import { authClient } from '@/common/auth/authClient';
import { Button } from '@/common/components/ui/button';

export function HelpButton() {
if (typeof window === 'undefined') return null;

const { data: session } = authClient.useSession();

const helpUrlSearchParams = new URLSearchParams({
subject: 'Feedback subject',
message: 'Share feedback or ask a question.',
...(session?.user.email && { email: session.user.email }),
...(session?.user.name && { name: session.user.name }),
});

return (
<div className="fixed right-6 bottom-6 z-50">
<Button
className="rounded-full bg-blue-600 shadow-lg transition-all duration-200 hover:bg-blue-700 hover:shadow-xl"
size="default"
asChild
>
<a href={`/contact?${helpUrlSearchParams.toString()}`} target="_blank" rel="noopener noreferrer">
<HelpCircle className="h-4 w-4" />
<span className="hidden text-xs sm:inline">Need help?</span>
<span className="text-xs sm:hidden">Help</span>
</a>
</Button>
</div>
);
}
import { HelpCircle } from 'lucide-react';

import { authClient } from '@/common/auth/authClient';
import { Button } from '@/common/components/ui/button';

export function HelpButton() {
if (typeof window === 'undefined') return null;

const { data: session } = authClient.useSession();

const helpUrlSearchParams = new URLSearchParams({
subject: 'Feedback subject',
message: 'Share feedback or ask a question.',
...(session?.user.email && { email: session.user.email }),
...(session?.user.name && { name: session.user.name }),
});

return (
<div className="fixed right-6 bottom-6 z-50">
<Button
className="rounded-full bg-blue-600 shadow-lg transition-all duration-200 hover:bg-blue-700 hover:shadow-xl"
size="default"
asChild
>
<a href={`/contact?${helpUrlSearchParams.toString()}`} target="_blank" rel="noopener noreferrer">
<HelpCircle className="h-4 w-4" />
<span className="hidden text-xs sm:inline">Need help?</span>
<span className="text-xs sm:hidden">Help</span>
</a>
</Button>
</div>
);
}
but of course still getting error in browser console
react-dom_client.js?v=f4cd0ec3:3354 Uncaught Error: Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:

- A server/client branch `if (typeof window !== 'undefined')`.
- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.
- Date formatting in a user's locale which doesn't match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.
...
react-dom_client.js?v=f4cd0ec3:3354 Uncaught Error: Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:

- A server/client branch `if (typeof window !== 'undefined')`.
- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.
- Date formatting in a user's locale which doesn't match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.
...
2 Replies
firm-tan
firm-tan4w ago
You can render HelpButton inside <ClientOnly /> like so
<ClientOnly>
<HelpButton />
</ClientOnly>
<ClientOnly>
<HelpButton />
</ClientOnly>
https://tanstack.com/router/latest/docs/framework/react/api/router/clientOnlyComponent
ClientOnly Component | TanStack Router React Docs
The ClientOnly component is used to render a components only in the client, without breaking the server-side rendering due to hydration errors. It accepts a fallback prop that will be rendered if the...
deep-jade
deep-jadeOP4w ago
nice exactly what I was looking for, thanks! btw since I don't want every consumer of this component to have to wrap it in <ClientOnly>, I just wrapped the whole component, seems to work well
import { ClientOnly, Link } from '@tanstack/react-router';
import { HelpCircle } from 'lucide-react';

import { authClient } from '@/common/auth/authClient';
import { Button } from '@/common/components/ui/button';

export function HelpButton() {
const { data: session } = authClient.useSession();

return (
<ClientOnly>
<div className="fixed right-6 bottom-6 z-50">
<Button
className="rounded-full bg-blue-600 shadow-lg transition-all duration-200 hover:bg-blue-700 hover:shadow-xl"
size="default"
asChild
>
<Link
to="/contact"
search={{
subject: 'Feedback subject',
message: 'Share feedback or ask a question.',
email: session?.user.email ?? '',
name: session?.user.name ?? '',
}}
target="_blank"
rel="noopener noreferrer"
>
<HelpCircle className="h-4 w-4" />
<span className="hidden text-xs sm:inline">Need help?</span>
<span className="text-xs sm:hidden">Help</span>
</Link>
</Button>
</div>
</ClientOnly>
);
}
import { ClientOnly, Link } from '@tanstack/react-router';
import { HelpCircle } from 'lucide-react';

import { authClient } from '@/common/auth/authClient';
import { Button } from '@/common/components/ui/button';

export function HelpButton() {
const { data: session } = authClient.useSession();

return (
<ClientOnly>
<div className="fixed right-6 bottom-6 z-50">
<Button
className="rounded-full bg-blue-600 shadow-lg transition-all duration-200 hover:bg-blue-700 hover:shadow-xl"
size="default"
asChild
>
<Link
to="/contact"
search={{
subject: 'Feedback subject',
message: 'Share feedback or ask a question.',
email: session?.user.email ?? '',
name: session?.user.name ?? '',
}}
target="_blank"
rel="noopener noreferrer"
>
<HelpCircle className="h-4 w-4" />
<span className="hidden text-xs sm:inline">Need help?</span>
<span className="text-xs sm:hidden">Help</span>
</Link>
</Button>
</div>
</ClientOnly>
);
}

Did you find this page helpful?