Building a Web NFC React Hook

Introduction

Managing Web NFC state (scanning, writing, errors, support) directly in your components can lead to messy code. Creating a custom React hook allows you to encapsulate this logic and reuse it across your application.

The useNfc Hook

Here is a simple implementation of a useNfc hook that handles reading tags.

import { useState, useEffect, useCallback } from 'react';

export function useNfc() {
  const [isSupported, setIsSupported] = useState(false);
  const [isScanning, setIsScanning] = useState(false);
  const [message, setMessage] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    if ('NDEFReader' in window) {
      setIsSupported(true);
    }
  }, []);

  const scan = useCallback(async () => {
    if (!isSupported) return;

    try {
      const ndef = new window.NDEFReader();
      await ndef.scan();
      setIsScanning(true);
      setError(null);

      ndef.onreading = (event) => {
        setMessage(event.message);
      };

      ndef.onreadingerror = () => {
        setError('Error reading tag.');
      };
    } catch (err) {
      setError(err.message);
      setIsScanning(false);
    }
  }, [isSupported]);

  return { isSupported, isScanning, message, error, scan };
}

Usage

You can now use this hook in any component:

export default function NfcReader() {
  const { isSupported, isScanning, message, scan, error } = useNfc();

  if (!isSupported) {
    return <p>Web NFC is not supported.</p>;
  }

  return (
    <div>
      <button onClick={scan} disabled={isScanning}>
        {isScanning ? 'Scanning...' : 'Start Scan'}
      </button>

      {error && <p>Error: {error}</p>}

      {message && <p>Tag detected!</p>}
    </div>
  );
}
✏️ Edit this page on GitHub