How I Write a 3D Book Component

Last Updated: January 6, 2025

While 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!