Framework Integration Guide

Copy-paste patterns for React, Next.js, and Vue 3

Step 1 — Install
npm install @mstechmalaysia/helios-fingerprint-sdk
ℹ️
The interactive demos on this site run without an API key. In production, pass your publicApiKey to load() to enable usage tracking and higher rate limits. The snippets below use YOUR_PUBLIC_API_KEY as a placeholder.
💡
The SDK accesses browser APIs (window, navigator), so it must run inside useEffect — never at the module or component top level, which would break during any server-side rendering step.
Custom hook — src/hooks/useFingerprint.js
useFingerprint.js
import { useState, useEffect } from 'react';
import { load } from '@mstechmalaysia/helios-fingerprint-sdk';

let heliosFpAgent;

function getHeliosFingerprint() {
  const agentPromise = heliosFpAgent
    ? Promise.resolve(heliosFpAgent)
    : load({ publicApiKey: 'YOUR_PUBLIC_API_KEY' }).then(fp => {
        heliosFpAgent = fp;
        return fp;
      });

  return agentPromise.then(fp => fp.get());
}

export function useFingerprint() {
  const [result, setResult] = useState(null);
  const [error,  setError]  = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;

    getHeliosFingerprint()
      .then(data => { if (!cancelled) setResult(data); })
      .catch(err  => { if (!cancelled) setError(err); })
      .finally(()  => { if (!cancelled) setLoading(false); });

    return () => { cancelled = true; }; // cleanup on unmount
  }, []);

  return { result, error, loading };
}
Component usage — src/components/FingerprintCard.jsx
FingerprintCard.jsx
import { useFingerprint } from '../hooks/useFingerprint';

export default function FingerprintCard() {
  const { result, error, loading } = useFingerprint();

  if (loading) return <p>Collecting fingerprint…</p>;
  if (error)   return <p>Error: {error.message}</p>;

  return (
    <dl>
      <dt>Device Core ID</dt>
      <dd>{result.deviceCoreId}</dd>

      <dt>Visitor ID</dt>
      <dd>{result.visitorId}</dd>

      <dt>VPN</dt>
      <dd>{result.flags.isVpn}</dd>

      <dt>Incognito</dt>
      <dd>{result.flags.isIncognito}</dd>
    </dl>
  );
}
⚠️
SSR caveat: The SDK calls window, document, and navigator — all unavailable on the server. Without a client-side guard your build will crash. Pick one of the two patterns below.
Option A — App Router · 'use client' component
app/components/Fingerprint.tsx
'use client'; // ← required — marks this as a Client Component

import { useState, useEffect } from 'react';
import { load } from '@mstechmalaysia/helios-fingerprint-sdk';

let heliosFpAgent;

function getHeliosFingerprint() {
  const agentPromise = heliosFpAgent
    ? Promise.resolve(heliosFpAgent)
    : load({ publicApiKey: 'YOUR_PUBLIC_API_KEY' }).then(fp => {
        heliosFpAgent = fp;
        return fp;
      });

  return agentPromise.then(fp => fp.get());
}

export default function Fingerprint() {
  const [result, setResult] = useState(null);

  useEffect(() => {
    getHeliosFingerprint().then(setResult);
  }, []);

  if (!result) return <p>Loading…</p>;

  return (
    <dl>
      <dt>Device Core ID</dt><dd>{result.deviceCoreId}</dd>
      <dt>Visitor ID</dt><dd>{result.visitorId}</dd>
      <dt>VPN</dt><dd>{result.flags.isVpn}</dd>
    </dl>
  );
}

// Then import it in any Server Component / page:
// import Fingerprint from '@/components/Fingerprint';
// <Fingerprint />  — Next.js will automatically render it client-side only
Option B — Pages Router · dynamic import with ssr: false
pages/index.tsx
import dynamic from 'next/dynamic';

// ssr: false ensures the component is only ever rendered in the browser
const Fingerprint = dynamic(
  () => import('../components/Fingerprint'),
  { ssr: false }
);

export default function Page() {
  return (
    <main>
      <h1>My App</h1>
      <Fingerprint />
    </main>
  );
}

// The Fingerprint component itself is the same 'use client' component from Option A
Which to pick? If you're on the App Router (Next.js 13+), use Option A — add 'use client' and drop the component anywhere. For the Pages Router, use Option B with dynamic(..., {'{'} ssr: false {'}'} ).
💡
Always call load() inside onMounted — this guarantees the SDK only runs in the browser. If you use Nuxt 3, wrap it in onMounted the same way; do not call it in setup() at the top level.
Composable — src/composables/useFingerprint.js
useFingerprint.js
import { ref, onMounted } from 'vue';
import { load } from '@mstechmalaysia/helios-fingerprint-sdk';

let heliosFpAgent;

async function getHeliosFingerprint() {
  if (!heliosFpAgent) {
    heliosFpAgent = await load({ publicApiKey: 'YOUR_PUBLIC_API_KEY' });
  }
  return heliosFpAgent.get();
}

export function useFingerprint() {
  const result  = ref(null);
  const error   = ref(null);
  const loading = ref(true);

  onMounted(async () => {
    try {
      result.value = await getHeliosFingerprint();
    } catch (err) {
      error.value = err;
    } finally {
      loading.value = false;
    }
  });

  return { result, error, loading };
}
Component usage — src/components/FingerprintCard.vue
FingerprintCard.vue
<script setup>
import { useFingerprint } from '../composables/useFingerprint';

const { result, error, loading } = useFingerprint();
</script>

<template>
  <p v-if="loading">Collecting fingerprint…</p>
  <p v-else-if="error">Error: {{ error.message }}</p>
  <dl v-else>
    <dt>Device Core ID</dt>
    <dd>{{ result.deviceCoreId }}</dd>

    <dt>Visitor ID</dt>
    <dd>{{ result.visitorId }}</dd>

    <dt>VPN</dt>
    <dd>{{ result.flags.isVpn }}</dd>

    <dt>Incognito</dt>
    <dd>{{ result.flags.isIncognito }}</dd>
  </dl>
</template>
⚠️
Nuxt 3: The same composable works as-is because onMounted never runs on the server. Do not use useAsyncData or useFetch with this SDK — those run server-side too.