Qr Code

Component

Interactive examples and API documentation

Basic
The most basic usage.
Code
<%= render HakumiComponents::QrCode::Component.new(
  value: "https://hakumi.com"
) %>
With Icon
QRCode with icon overlay.
Code
<%= render HakumiComponents::QrCode::Component.new(
  value: "https://hakumi.com",
  icon: :scan,
  icon_size: 48
) %>
Status
The status can be controlled by the value status.
Active
Loading
Expired
Scanned
Code
<%= render HakumiComponents::Space::Component.new(size: :large) do |space| %>
  <% [ :active, :loading, :expired, :scanned ].each do |state| %>
    <% space.with_item do %>
      <%= render HakumiComponents::Space::Component.new(direction: :vertical, size: :small) do |status_space| %>
        <% status_space.with_item do %>
          <%= render(HakumiComponents::Typography::Text::Component.new(weight: :strong)) { state.to_s.capitalize } %>
        <% end %>
        <% status_space.with_item do %>
          <%= render HakumiComponents::QrCode::Component.new(
            value: "https://hakumi.com/#{state}",
            status: state
          ) %>
        <% end %>
      <% end %>
    <% end %>
  <% end %>
<% end %>
Custom Status Render
Customize the rendering logic of the QR code in different states.
Code
<%= render HakumiComponents::QrCode::Component.new(
  value: "https://hakumi.com/custom-status",
  status: :expired,
  status_render: ->(status) do
    render HakumiComponents::Space::Component.new(direction: :vertical, size: :small) do |space|
      space.with_item do
        render(HakumiComponents::Typography::Text::Component.new(weight: :strong)) { "Status: #{status}" }
      end
      space.with_item do
        render(HakumiComponents::Button::Component.new(type: :primary, size: :small)) { "Refresh" }
      end
    end
  end
) %>
Custom Render Type
Customize rendering by type, providing canvas and svg.
Canvas
SVG
Code
<%= render HakumiComponents::Space::Component.new(size: :large) do |space| %>
  <% space.with_item do %>
    <%= render HakumiComponents::Space::Component.new(direction: :vertical, size: :small) do |type_space| %>
      <% type_space.with_item do %>
        <%= render(HakumiComponents::Typography::Text::Component.new(weight: :strong)) { "Canvas" } %>
      <% end %>
      <% type_space.with_item do %>
        <%= render HakumiComponents::QrCode::Component.new(
          value: "https://hakumi.com/canvas",
          type: :canvas
        ) %>
      <% end %>
    <% end %>
  <% end %>

  <% space.with_item do %>
    <%= render HakumiComponents::Space::Component.new(direction: :vertical, size: :small) do |type_space| %>
      <% type_space.with_item do %>
        <%= render(HakumiComponents::Typography::Text::Component.new(weight: :strong)) { "SVG" } %>
      <% end %>
      <% type_space.with_item do %>
        <%= render HakumiComponents::QrCode::Component.new(
          value: "https://hakumi.com/svg",
          type: :svg
        ) %>
      <% end %>
    <% end %>
  <% end %>
<% end %>
Custom Size
Adjust the size of the QR code.
Resize with JS API
Code
<%= render HakumiComponents::Space::Component.new(size: :large) do |space| %>
  <% space.with_item do %>
    <%= render HakumiComponents::QrCode::Component.new(
      value: "https://hakumi.com/size-small",
      size: 120
    ) %>
  <% end %>

  <% space.with_item do %>
    <%= render HakumiComponents::QrCode::Component.new(
      value: "https://hakumi.com/size-large",
      size: 220
    ) %>
  <% end %>

  <% space.with_item do %>
    <%= render HakumiComponents::Space::Component.new(direction: :vertical, size: :small, block: true) do |interactive| %>
      <% interactive.with_item do %>
        <%= render HakumiComponents::Typography::Text::Component.new(strong: true) do %>
          Resize with JS API
        <% end %>
      <% end %>

      <% interactive.with_item do %>
        <%= render HakumiComponents::QrCode::Component.new(
          id: "qr-code-resize",
          value: "https://hakumi.com/resize",
          size: 160,
          type: :canvas
        ) %>
      <% end %>

      <% interactive.with_item do %>
        <%= render HakumiComponents::Space::Component.new(size: :small) do |buttons| %>
          <% [ { label: "120px", size: 120 }, { label: "200px", size: 200 }, { label: "260px", size: 260 } ].each do |option| %>
            <% buttons.with_item do %>
              <%= render(HakumiComponents::Button::Component.new(
                type: :default,
                data: { "qr-resize": option[:size] }
              )) { option[:label] } %>
            <% end %>
          <% end %>
        <% end %>
      <% end %>
    <% end %>
  <% end %>
<% end %>

<script>
  (function() {
    var target = document.getElementById("qr-code-resize")
    if (!target) return

    var buttons = document.querySelectorAll("[data-qr-resize]")
    if (!buttons.length) return

    var wire = function() {
      if (!target.hakumiComponent?.api || typeof target.hakumiComponent?.api.setSize !== "function") {
        return false
      }

      buttons.forEach(function(button) {
        button.addEventListener("click", function() {
          var nextSize = Number(button.getAttribute("data-qr-resize"))
          target.hakumiComponent?.api.setSize(nextSize)
        })
      })

      return true
    }

    if (wire()) return

    var onReady = function() {
      if (wire()) {
        document.removeEventListener("turbo:load", onReady)
        window.removeEventListener("load", onReady)
      }
    }

    document.addEventListener("turbo:load", onReady)
    window.addEventListener("load", onReady)
  })()
</script>
Custom Color
Customize foreground and background colors.
Code
<%= render HakumiComponents::QrCode::Component.new(
  value: "https://hakumi.com/color",
  color: "#1677ff",
  bg_color: "#e6f4ff"
) %>
Download QRCode
Download the QRCode image via the public API.
Code
<%= render HakumiComponents::Space::Component.new(direction: :vertical, size: :default) do |space| %>
  <% space.with_item do %>
    <%= render HakumiComponents::QrCode::Component.new(
      value: "https://hakumi.com/download",
      id: "qr-code-download"
    ) %>
  <% end %>
  <% space.with_item do %>
    <%= render(HakumiComponents::Button::Component.new(id: "qr-code-download-button", type: :primary)) { "Download QRCode" } %>
  <% end %>
<% end %>

<script>
  (() => {
    const button = document.getElementById("qr-code-download-button")
    const qr = document.getElementById("qr-code-download")
    if (!button || !qr) return

    const wire = () => {
      if (!qr.hakumiComponent?.api) return false
      button.addEventListener("click", () => qr.hakumiComponent?.api.download("hakumi-qr-code"))
      return true
    }

    if (wire()) return

    const onReady = () => {
      if (wire()) {
        document.removeEventListener("turbo:load", onReady)
        window.removeEventListener("load", onReady)
      }
    }

    document.addEventListener("turbo:load", onReady)
    window.addEventListener("load", onReady)
  })()
</script>
Error Level
Set the error correction level.
Level L
Level M
Level Q
Level H
Code
<%= render HakumiComponents::Space::Component.new(size: :large) do |space| %>
  <% [ :L, :M, :Q, :H ].each do |level| %>
    <% space.with_item do %>
      <%= render HakumiComponents::Space::Component.new(direction: :vertical, size: :small) do |level_space| %>
        <% level_space.with_item do %>
          <%= render(HakumiComponents::Typography::Text::Component.new(weight: :strong)) { "Level #{level}" } %>
        <% end %>
        <% level_space.with_item do %>
          <%= render HakumiComponents::QrCode::Component.new(
            value: "https://hakumi.com/level-#{level}",
            error_level: level
          ) %>
        <% end %>
      <% end %>
    <% end %>
  <% end %>
<% end %>
With Popover
Advanced usage with popover.
Code
<%= render HakumiComponents::Popover::Component.new(
  title: "Scan me",
  body: render(HakumiComponents::QrCode::Component.new(value: "https://hakumi.com/popover")),
  trigger: "hover",
  placement: "bottom"
) do %>
  <%= render(HakumiComponents::Button::Component.new(type: :primary)) { "Hover to view" } %>
<% end %>
Programmatic
Render a QRCode with HakumiComponents.renderComponent.
Awaiting render...
Code
<%= render HakumiComponents::Space::Component.new(direction: :vertical, size: :default) do |space| %>
  <% space.with_item do %>
    <%= render(HakumiComponents::Button::Component.new(id: "qr-code-programmatic-button", type: :primary)) { "Render QRCode" } %>
  <% end %>
  <% space.with_item do %>
    <%= render HakumiComponents::Flex::Component.new(
      id: "qr-code-programmatic-target",
      align: :center,
      justify: :center,
      style: "min-height: 180px;"
    ) do %>
      <%= render(HakumiComponents::Typography::Text::Component.new(type: :secondary)) { "Awaiting render..." } %>
    <% end %>
  <% end %>
<% end %>

<script>
  (() => {
    const button = document.getElementById("qr-code-programmatic-button")
    const target = document.getElementById("qr-code-programmatic-target")
    if (!button || !target) return

    const wire = () => {
      if (!window.HakumiComponents?.renderComponent) return false

      button.addEventListener("click", async () => {
        await window.HakumiComponents.renderComponent("qr_code", {
          target,
          params: {
            value: "https://hakumi.com/render",
            size: 180,
            type: "svg",
            error_level: "H"
          }
        })
      })

      return true
    }

    if (wire()) return

    const onReady = () => {
      if (wire()) {
        document.removeEventListener("turbo:load", onReady)
        window.removeEventListener("load", onReady)
      }
    }

    document.addEventListener("turbo:load", onReady)
    window.addEventListener("load", onReady)
  })()
</script>

QRCode Ruby Props

Prop Type Default Description
value String - Value encoded in the QR code.
size Number 160 QR code size in pixels.
type Symbol :canvas Render type (:canvas or :svg).
color String #000000d9 Foreground color.
bg_color String #ffffff Background color.
icon String - Icon URL overlay.
icon_size Number 40 Icon size in pixels.
status Symbol :active Status (:active, :expired, :loading, :scanned).
status_render Proc | ViewComponent or String - Custom status render callback or component.
bordered Boolean true Whether to display a border.
error_level Symbol :M Error correction level (:L, :M, :Q, :H).
download_name String - Default filename for downloads.
aria_label String QR Code Accessibility label.

I18n Keys

The component uses Rails I18n for status overlays. Override via I18n config with scope hakumi_components.qr_code.
Prop Type Default Description
aria_label String "QR Code" Accessibility label for the QR code.
status.expired String "Expired" Status text when QR code is expired.
status.loading String "Loading" Status text when QR code is loading.
status.scanned String "Scanned" Status text when QR code is scanned.
status.active String "Active" Status text when QR code is active.
description.expired String "Please refresh to continue" Description shown for expired status.
description.scanned String "You can now continue" Description shown for scanned status.

JavaScript API

Access via element.hakumiComponent.api
Prop Type Default Description
getValue() Method - Returns the current QR value.
setValue(value) Method - Updates the QR value and re-renders.
getSize() Method - Returns the current size in pixels.
setSize(size) Method - Sets the size (px) and updates layout + render.
getType() Method - Returns the renderer type (:canvas or :svg).
setType(type) Method - Switches renderer between canvas / svg.
setColors({ color, bgColor }) Method - Overrides foreground/background colors.
setErrorLevel(level) Method - Changes error correction level (:L/:M/:Q/:H).
getDataUrl() Method - Returns a data URL for the rendered QR code.
download(filename) Method - Downloads the QR code image.
rerender() Method - Forces a re-render of the QR code.
getState() Method - Returns { value, size, type, errorLevel, status }.