Image

Component

Interactive examples and API documentation

Basic Usage
Click the image to zoom in.
Paris cityscape
Preview
Code
<%= render Hakumi::Image::Component.new(
  src: "https://images.unsplash.com/photo-1499856871958-5b9627545d1a?w=400",
  alt: "Paris cityscape",
  width: 200
) %>
Fault Tolerant
Load failed to display image placeholder using the fallback prop.
Image with fallback (Venice)
Preview
Broken image (no fallback)
Code
<%= render Hakumi::Space::Component.new(size: :large) do |space| %>
  <% space.with_item do %>
    <%= render Hakumi::Image::Component.new(
      src: "https://invalid-url-for-testing.com/broken.png",
      fallback: "https://images.unsplash.com/photo-1523906834658-6e24ef2386f9?w=400",
      alt: "Image with fallback (Venice)",
      width: 200
    ) %>
  <% end %>
  <% space.with_item do %>
    <%= render Hakumi::Image::Component.new(
      src: "https://invalid-url-for-testing.com/broken.png",
      alt: "Broken image (no fallback)",
      width: 200,
      preview: false
    ) %>
  <% end %>
<% end %>
Progressive Loading
Progressive loading with blur placeholder while image loads.
High Resolution Universe
Preview
Code
<div style="display: flex; flex-direction: column; gap: 16px; align-items: start;">
  <%= render Hakumi::Image::Component.new(
    src: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?q=80&w=4000&auto=format&fit=crop",
    alt: "High Resolution Universe",
    width: 400,
    placeholder: true,
    id: "progressive-demo-image"
  ) %>

  <%= render Hakumi::Button::Component.new(type: :primary, id: "progressive-demo-reload") do %>
    Reload Image
  <% end %>
</div>

<script>
  (() => {
    const button = document.getElementById("progressive-demo-reload")
    const imageWrapper = document.getElementById("progressive-demo-image")
    if (!button || !imageWrapper) return

    button.addEventListener("click", () => {
      const img = imageWrapper.querySelector("img")
      if (!img) return

      imageWrapper.classList.remove("hakumi-image-loaded", "hakumi-image-error")
      const baseUrl = img.src.split("&t=")[0]
      img.src = `${baseUrl}&t=${Date.now()}`
    })
  })()
</script>
Multiple Image Preview
Click the left and right switch buttons to preview multiple images.
Paris
Venice
Rome
Amsterdam
Code
<%= render Hakumi::Image::PreviewGroup::Component.new do |group| %>
  <% group.with_image(
    src: "https://images.unsplash.com/photo-1499856871958-5b9627545d1a?w=200&h=200&fit=crop",
    alt: "Paris",
    width: 150
  ) %>
  <% group.with_image(
    src: "https://images.unsplash.com/photo-1523906834658-6e24ef2386f9?w=200&h=200&fit=crop",
    alt: "Venice",
    width: 150
  ) %>
  <% group.with_image(
    src: "https://images.unsplash.com/photo-1552832230-c0197dd311b5?w=200&h=200&fit=crop",
    alt: "Rome",
    width: 150
  ) %>
  <% group.with_image(
    src: "https://images.unsplash.com/photo-1534351590666-13e3e96b5017?w=200&h=200&fit=crop",
    alt: "Amsterdam",
    width: 150
  ) %>
<% end %>
Custom Preview Image
You can set a different preview image (higher resolution) using preview_src.
Paris - Eiffel Tower (thumbnail shows small, preview shows large)
Preview
Code
<%= render Hakumi::Image::Component.new(
  src: "https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=200&h=150&fit=crop",
  preview_src: "https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=1200",
  alt: "Paris - Eiffel Tower (thumbnail shows small, preview shows large)",
  width: 200
) %>
Controlled Preview
You can control preview programmatically using the JavaScript API.
Amsterdam canals
Preview
Code
<style>
  .controlled-preview-demo {
    display: flex;
    align-items: center;
    gap: 16px;
  }
</style>

<div class="controlled-preview-demo">
  <%= render Hakumi::Image::Component.new(
    src: "https://images.unsplash.com/photo-1534351590666-13e3e96b5017?w=400",
    alt: "Amsterdam canals",
    width: 200,
    id: "controlled-image"
  ) %>

  <%= render Hakumi::Button::Component.new(
    type: :primary,
    id: "open-preview-btn"
  ) do %>
    Open Preview Programmatically
  <% end %>
</div>

<script>
  (() => {
    const button = document.getElementById("open-preview-btn")
    const image = document.getElementById("controlled-image")
    if (!button || !image) return

    const wire = () => {
      const api = image.hakumiComponent?.api
      if (!api) return false

      button.addEventListener("click", () => api.open())
      return true
    }

    if (wire()) return

    const onRegister = ({ detail }) => {
      if (detail.id !== image.id) return
      if (wire()) window.removeEventListener("hakumi-component:registered", onRegister)
    }

    window.addEventListener("hakumi-component:registered", onRegister)
  })()
</script>
Custom Preview Mask
Customize the preview mask content by passing a block.
Barcelona - custom mask example
Click to Preview
Code
<%= render Hakumi::Image::Component.new(
  src: "https://images.unsplash.com/photo-1467269204594-9661b134dd2b?w=400",
  alt: "Barcelona - custom mask example",
  width: 200
) do %>
  <div style="display: flex; align-items: center; gap: 8px;">
    <%= render Hakumi::Icon::Component.new(name: :eye, size: 16) %>
    <span>Click to Preview</span>
  </div>
<% end %>
Mask Closable
Set mask_closable: true to close the preview when clicking on the background.
Preview
Code
<%= render Hakumi::Image::Component.new(
  width: 200,
  src: "https://images.unsplash.com/photo-1579546929518-9e396f3cc809?q=80&w=2070&auto=format&fit=crop",
  mask_closable: true
) %>
Disable Preview
Set preview: false to disable the preview feature.
London - Tower Bridge
Code
<%= render Hakumi::Image::Component.new(
  src: "https://images.unsplash.com/photo-1513635269975-59663e0ac1ad?w=400",
  alt: "London - Tower Bridge",
  width: 200,
  preview: false
) %>
Nested in Modal
Image preview works correctly when nested inside a modal dialog.
Code
<%= render Hakumi::Button::Component.new(
  type: :primary,
  id: "open-modal-btn"
) do %>
  Open Modal with Image
<% end %>

<%= render Hakumi::Modal::Component.new(
  id: "nested-image-modal",
  title: "Image Preview in Modal",
  width: 600,
  open: false
) do %>
  <div style="text-align: center;">
    <%= render Hakumi::Image::Component.new(
      src: "https://images.unsplash.com/photo-1549144511-f099e773c147?w=500",
      alt: "Paris - Louvre",
      width: 400,
      id: "nested-image"
    ) %>
  </div>
<% end %>

<script>
  (() => {
    const button = document.getElementById("open-modal-btn")
    const modal = document.getElementById("nested-image-modal")
    if (!button || !modal) return

    const wire = () => {
      const api = modal.hakumiComponent?.api
      if (!api) return false

      button.addEventListener("click", () => api.open())
      return true
    }

    if (wire()) return

    const onRegister = ({ detail }) => {
      if (detail.id !== modal.id) return
      if (wire()) window.removeEventListener("hakumi-component:registered", onRegister)
    }

    window.addEventListener("hakumi-component:registered", onRegister)
  })()
</script>

Image API

Prop Type Default Description
src String - Image source URL (required).
alt String - Alt text for accessibility.
width Integer or String - Image width.
height Integer or String - Image height.
fallback String - Fallback image URL shown on load error.
placeholder Boolean false Show blur placeholder while loading.
preview Boolean true Enable preview on click.
preview_src String - Alternate (higher resolution) image for preview.
content slot Block - Custom mask content.
**html_options Keyword args - HTML attributes merged into wrapper.

Image.PreviewGroup API

Prop Type Default Description
items Array - Array of image sources or hashes with src/alt keys.
preview Boolean true Enable preview for the group.
with_image Slot - Add images using with_image with Image component props.
**html_options Keyword args - HTML attributes merged into wrapper.

JavaScript API

Prop Type Default Description
element.hakumiImage.open() Method - Opens the preview modal.
element.hakumiImage.close() Method - Closes the preview modal.
element.hakumiImage.zoomIn() Method - Zooms in the preview image.
element.hakumiImage.zoomOut() Method - Zooms out the preview image.
element.hakumiImage.rotateLeft() Method - Rotates the image 90° counter-clockwise.
element.hakumiImage.rotateRight() Method - Rotates the image 90° clockwise.
element.hakumiImage.flipX() Method - Flips the image horizontally.
element.hakumiImage.flipY() Method - Flips the image vertically.
element.hakumiImage.reset() Method - Resets all transformations.