How I Write a 3D Book Component
Last Updated: January 6, 2025While building this 3D book component, I explored how modern React features, CSS transforms, and animation libraries can work together to create an engaging UI element. Let's dive into the implementation details and understand how each piece contributes to the final result.
Play around with the component
Try hovering over these examples to see how they work:
Simple Design
Clean, minimal aesthetic
basic style
How the Book Component Works
To make a book that feels real, we need to think about how actual books are made. Here's how we break it down in code:
1. The Container: This is like the stage where our book performs - it sets up the 3D space where everything happens
2. The Moving Part: This handles all the cool rotation effects when you hover over the book
3. The Layers: Just like a real book, we have:
- A front cover that shows the title
- The spine where all pages connect
- The pages on the side
- A back cover to complete the look
Setting Up Our Types
First, let's define what options our book can have. This helps us catch errors early and makes the component easier to use:
// These control where things go
type ContentPosition = "top" | "center" | "bottom"
type ContentAlignment = "left" | "center" | "right"
type BookVariant = "simple" | "gradient"
// How we want to show the content
interface ContentConfig {
position?: ContentPosition // Where to put it
align?: ContentAlignment // How to align it
maxLines?: number // How many lines to show
truncate?: boolean // Cut off long text
className?: string // Custom styling
}
// All the things we can customize
interface BookProps {
// The basics
title: string // What's on the cover
description?: string // More details
size?: "sm" | "md" | "lg" // How big it should be
// How it looks
coverColor?: string // What color to use
gradientFrom?: string // Start color for gradient
gradientTo?: string // End color for gradient
contentColor?: string // Text color
variant?: BookVariant // Which style to use
// Extra features
illustration?: React.ReactNode // Add an image
footer?: string // Text at the bottom
}
The Numbers That Make It Work
These are the measurements and values that make our book look just right:
// The main settings
const BOOK_CONSTANTS = {
ASPECT_RATIO: "49/60", // Makes it look like a real book
BIND_OPACITY: 0.5, // How see-through the spine is
DEFAULT_COLOR: "#1A1A1A", // Fallback color
// Different sizes we can use
SIZES: {
sm: 150, // Small book
md: 180, // Regular book
lg: 200 // Big book
}
}
// Helper functions for sizing
const getPageDepth = (size: BookSize) => {
const baseWidth = BOOK_CONSTANTS.SIZES[size]
// Make sure it's not too thin
return Math.max(Math.floor(baseWidth * 0.15), 38)
}
const getPageWidth = (size: BookSize) => {
const baseWidth = BOOK_CONSTANTS.SIZES[size]
// Keep the pages visible
return Math.max(Math.floor(baseWidth * 0.1), 29)
}
Making It Move in 3D
Here's how we create that smooth 3D effect when you hover over the book:
// Set up the 3D space
<div style={{ perspective: "1200px" }}>
{/* This part handles the movement */}
<motion.div
style={{
transformStyle: "preserve-3d",
originX: "left", // Rotate from the left edge
}}
whileHover={{
rotateY: -20, // Tilt when hovering
scale: 1.066, // Grow slightly
x: -8, // Shift a bit left
transition: {
type: "spring", // Bounce effect
stiffness: 300, // How springy
damping: 20 // How quickly it settles
}
}}
>
{/* All the book parts */}
<div style={{ transformStyle: "preserve-3d" }}>
{/* Front cover */}
<div style={{ transform: "translateZ(2px)" }} />
{/* Spine */}
<div style={{ transform: "translateZ(3px)" }} />
{/* Pages */}
<div style={{ transform: getPageTransform() }} />
{/* Back cover */}
<div style={{ transform: `translateZ(-${getPageDepth()}px)` }} />
</div>
</motion.div>
</div>
Handling the Content
Here's how we organize all the text and images on the book:
// Figure out where to put things
const getContentPosition = (pos?: ContentPosition) => {
switch (pos) {
case "top": return "items-start" // At the top
case "bottom": return "items-end" // At the bottom
default: return "items-center" // In the middle
}
}
// How to align the text
const getContentAlignment = (align?: ContentAlignment) => {
switch (align) {
case "right": return "text-right" // Right side
case "center": return "text-center" // Centered
default: return "text-left" // Left side
}
}
// Putting it all together
<div className={cn(
"absolute inset-0 flex flex-col p-6",
getContentPosition(titleConfig.position),
getContentAlignment(titleConfig.align)
)}>
{/* The title */}
<div className={titleConfig.className}>
<h3 style={{ color: contentColor }}>{title}</h3>
{description && (
<p className="text-xs opacity-80">{description}</p>
)}
</div>
{/* Footer text */}
{footer && (
<p className="text-xs opacity-60 mt-auto">
{footer}
</p>
)}
</div>
Making It Look Good
These are the tricks we use to make the book look more realistic:
// Creating gradients
const getBackground = () => {
if (variant === "gradient" && gradientFrom && gradientTo) {
return `linear-gradient(
to bottom right,
${gradientFrom},
${gradientTo}
)`
}
return coverColor
}
// Adding shadows to the spine
const bindShadow = `
inset -1px 0 2px rgba(0,0,0,0.1), // Inner shadow
1px 0 1px rgba(255,255,255,0.05) // Light edge
`
// Making the pages look real
const pageGradient = hasPageGradient
? `linear-gradient(
90deg,
${pageColor},
transparent 70%
)`
: pageColor
Ways to Use It
Here are some examples of how you can use the book component:
// Simple book
<Book
title="Design Systems"
coverColor="#4A90E2"
size="md"
/>
// Fancy gradient book
<Book
title="Advanced Patterns"
gradientFrom="#8B5CF6"
gradientTo="#3B82F6"
variant="gradient"
description="Modern component architecture"
titleConfig={{
position: "top",
align: "left"
}}
footer="By Design Team"
/>
// Book with an icon
<Book
title="React Patterns"
coverColor="#2A4365"
illustration={<ReactIcon />}
illustrationAlign="center"
contentColor="#ffffff"
/>
That's how we built this 3D book component! It shows how we can use modern tools like Framer Motion and Tailwind to create something that looks great and works smoothly. The key is breaking down the complex parts into smaller, manageable pieces and thinking about how they work together.
If you have any questions or ideas for how to improve this component, feel free to reach out on Twitter!