While testing my application on mobile screens, I noticed that the <Tooltip>
component from Shadcn UI doesn't activate or display as expected on smaller devices. Tapping or long-pressing the tooltip trigger will not show the tooltip, which is what I would like the behaviour to be.
Is there a way to ensure the <Tooltip>
component activates on tap on mobile screens?
Essentially, the <Tooltip>
component won't show on mobile screens, but a <Popover>
component will! You can make a <HybridTooltip>
component that will show a Tooltip on desktop, and a Popover on mobile.
First, add hybrid-tooltip.tsx
to your ui folder, with the following code (adapted from Github):
'use client';
import { PropsWithChildren, createContext, useContext, useEffect, useState } from 'react';
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './tooltip';
import { Popover, PopoverTrigger, PopoverContent } from './popover';
import { TooltipContentProps, TooltipProps, TooltipProviderProps, TooltipTriggerProps } from '@radix-ui/react-tooltip';
import { PopoverContentProps, PopoverProps, PopoverTriggerProps } from '@radix-ui/react-popover';
const TouchContext = createContext<boolean | undefined>(undefined);
const useTouch = () => useContext(TouchContext);
const TouchProvider = (props: PropsWithChildren) => {
const [isTouch, setTouch] = useState<boolean>();
useEffect(() => {
setTouch(window.matchMedia('(pointer: coarse)').matches);
}, []);
return <TouchContext.Provider value={isTouch} {...props} />;
};
const HybridTooltipProvider = (props: TooltipProviderProps) => {
return <TooltipProvider delayDuration={0} {...props} />
}
const HybridTooltip = (props: TooltipProps & PopoverProps) => {
const isTouch = useTouch();
return isTouch ? <Popover {...props} /> : <Tooltip {...props} />;
};
const HybridTooltipTrigger = (props: TooltipTriggerProps & PopoverTriggerProps) => {
const isTouch = useTouch();
return isTouch ? <PopoverTrigger {...props} /> : <TooltipTrigger {...props} />;
};
const HybridTooltipContent = (props: TooltipContentProps & PopoverContentProps) => {
const isTouch = useTouch();
return isTouch ? <PopoverContent {...props} /> : <TooltipContent {...props} />;
};
export { TouchProvider, HybridTooltipProvider, HybridTooltip, HybridTooltipTrigger, HybridTooltipContent }
Then, wrap your app with <TouchProvider>
in your RootLayout:
import { TouchProvider } from '@/components/ui/hybrid-tooltip';
import './globals.css';
export default function RootLayout({ children }: React.PropsWithChildren) {
return (
<html lang='en'>
<body>
<TouchProvider>
<main>{children}</main>
</TouchProvider>
</body>
</html>
);
}
Finally, you can use the <HybridTooltip>
component, in place of a regular Tooltip:
import { HybridTooltipProvider, HybridTooltip, HybridTooltipTrigger, HybridTooltipContent } from "./ui/hybrid-tooltip";
// ...
<HybridTooltipProvider>
<HybridTooltip>
<HybridTooltipTrigger>
Trigger text
</HybridTooltipTrigger>
<HybridTooltipContent>
<p>Tooltip Contents</p>
</HybridTooltipContent>
</HybridTooltip>
</HybridTooltipProvider>