useIsMobile
The useIsMobile hook detects whether the current device is mobile by checking if the viewport width is less than 768px. It listens to window resize events and updates automatically.
Installation
npm install @craft-ui/hooks
import { useIsMobile } from "@craft-ui/hooks";
import { useIsMobile } from "@craft-ui/hooks";
function ResponsiveComponent() {
const isMobile = useIsMobile();
return (
<div>
{isMobile ? (
<MobileNavigation />
) : (
<DesktopNavigation />
)}
</div>
);
}
Returns
A boolean value indicating whether the viewport width is less than 768px. Returns false during server-side rendering and updates after mounting on the client.
Type Definition
function useIsMobile(): boolean;
Breakpoint
The hook uses a mobile breakpoint of 768px:
isMobile = true when viewport width is < 768px
isMobile = false when viewport width is >= 768px
Examples
Responsive Navigation
import { useIsMobile } from "@craft-ui/hooks";
import { useState } from "react";
function Navigation() {
const isMobile = useIsMobile();
const [isMenuOpen, setIsMenuOpen] = useState(false);
if (isMobile) {
return (
<nav>
<button onClick={() => setIsMenuOpen(!isMenuOpen)}>
Menu
</button>
{isMenuOpen && (
<ul>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
)}
</nav>
);
}
return (
<nav>
<ul style={{ display: "flex", gap: "1rem" }}>
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
</nav>
);
}
Conditional Component Rendering
import { useIsMobile } from "@craft-ui/hooks";
function Dashboard() {
const isMobile = useIsMobile();
return (
<div>
<h1>Dashboard</h1>
{isMobile ? (
<MobileCharts />
) : (
<DesktopCharts />
)}
</div>
);
}
Different Layouts
import { useIsMobile } from "@craft-ui/hooks";
function ProductGrid({ products }) {
const isMobile = useIsMobile();
return (
<div
style={{
display: "grid",
gridTemplateColumns: isMobile ? "1fr" : "repeat(3, 1fr)",
gap: isMobile ? "1rem" : "2rem",
}}
>
{products.map(product => (
<ProductCard key={product.id} product={product} compact={isMobile} />
))}
</div>
);
}
Touch vs Click Events
import { useIsMobile } from "@craft-ui/hooks";
function InteractiveElement() {
const isMobile = useIsMobile();
const [isActive, setIsActive] = useState(false);
const handleInteraction = () => {
setIsActive(!isActive);
};
return (
<div
onClick={isMobile ? undefined : handleInteraction}
onTouchStart={isMobile ? handleInteraction : undefined}
className={isActive ? "active" : ""}
>
{isMobile ? "Tap me" : "Click me"}
</div>
);
}
Responsive Image Loading
import { useIsMobile } from "@craft-ui/hooks";
function HeroImage() {
const isMobile = useIsMobile();
const imageSrc = isMobile
? "/images/hero-mobile.jpg"
: "/images/hero-desktop.jpg";
return (
<img
src={imageSrc}
alt="Hero"
style={{
width: "100%",
height: isMobile ? "300px" : "600px",
objectFit: "cover",
}}
/>
);
}
Modal Behavior
import { useIsMobile } from "@craft-ui/hooks";
function Modal({ isOpen, onClose, children }) {
const isMobile = useIsMobile();
if (!isOpen) return null;
return (
<div className="modal-overlay" onClick={onClose}>
<div
className="modal-content"
onClick={(e) => e.stopPropagation()}
style={{
width: isMobile ? "100%" : "600px",
height: isMobile ? "100vh" : "auto",
maxHeight: isMobile ? "none" : "90vh",
borderRadius: isMobile ? 0 : "8px",
}}
>
{children}
<button onClick={onClose}>Close</button>
</div>
</div>
);
}
Responsive Table
import { useIsMobile } from "@craft-ui/hooks";
function DataTable({ data }) {
const isMobile = useIsMobile();
if (isMobile) {
// Card layout for mobile
return (
<div>
{data.map(item => (
<div key={item.id} className="data-card">
<div><strong>Name:</strong> {item.name}</div>
<div><strong>Email:</strong> {item.email}</div>
<div><strong>Status:</strong> {item.status}</div>
</div>
))}
</div>
);
}
// Table layout for desktop
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{data.map(item => (
<tr key={item.id}>
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.status}</td>
</tr>
))}
</tbody>
</table>
);
}
Common Patterns
Combining with CSS Classes
function Container() {
const isMobile = useIsMobile();
return (
<div className={isMobile ? "container-mobile" : "container-desktop"}>
{/* Content */}
</div>
);
}
Conditional Hook Calls
function Component() {
const isMobile = useIsMobile();
// Use different hooks based on device
const orientation = isMobile ? useOrientation() : null;
return <div>{/* Content */}</div>;
}
Performance Optimization
function OptimizedComponent() {
const isMobile = useIsMobile();
// Only load heavy component on desktop
const HeavyComponent = isMobile ? null : lazy(() => import("./HeavyComponent"));
return (
<div>
{!isMobile && HeavyComponent && (
<Suspense fallback={<Spinner />}>
<HeavyComponent />
</Suspense>
)}
</div>
);
}
- The hook returns
false during server-side rendering
- The initial state is
undefined, which is coerced to false by the !! operator
- The hook automatically updates when the window is resized
- Event listeners are properly cleaned up when the component unmounts
- The 768px breakpoint is commonly used as the mobile/tablet boundary
- For more complex responsive logic, consider using
useMediaQuery hook instead
- The hook uses
window.matchMedia for efficient media query matching
- This hook is ideal for simple mobile/desktop detection; for more granular control, use custom media queries