npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

usegamigameapi

v1.0.15

Published

A React hook for managing interactive quiz/game flows through iframe communication using postMessage. This hook handles question flow, answer submission, result tracking, and seamless communication between a parent window and an iframe.

Downloads

62

Readme

useGameAPI

A React hook for managing interactive quiz/game flows through iframe communication using postMessage. This hook handles question flow, answer submission, result tracking, and seamless communication between a parent window and an iframe.

Installation

npm install usegamigameapi

Requirements

  • React >= 18

Basic Usage

import { useGameAPI } from "usegamigameapi";

function QuizComponent() {
  const {
    quiz,
    username,
    selectedAnswer,
    handleAnswerSelect,
    updateAnswer,
    handleContinue,
    isCompleted,
    currentResult,
  } = useGameAPI({
    onAnswerCorrect: ({ currentQuestionIndex }) => {
      console.log("Correct answer at question", currentQuestionIndex);
      // Trigger confetti or other celebrations
    },
    onAnswerIncorrect: () => {
      console.log("Incorrect answer");
    },
  });

  // Render your quiz UI using the returned state and methods
}

API Documentation

Hook Parameters

The useGameAPI hook accepts an object with the following optional callbacks:

| Parameter | Type | Description | | ------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | | onAnswerCorrect | (params: { currentQuestionIndex: number; correctAnswerId?: number; incorrectAnswerId?: number }) => void | Callback fired when the user submits a correct answer | | onAnswerIncorrect | (params: { currentQuestionIndex: number; correctAnswerId?: number; incorrectAnswerId?: number }) => void | Callback fired when the user submits an incorrect answer |

Return Values

The hook returns an object with the following properties:

State Data

| Property | Type | Description | | ---------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | quiz | any | The current question data received from the parent window (contains text, audio URL, answers array, etc.) | | currentResult | any | The result object after submitting an answer (contains isCorrect, isLastQuestion, correctAnswerId, incorrectAnswerId, and explanation) | | answers | (boolean \| null)[] | Array of answer history where each index corresponds to a question (true = correct, false = incorrect, null = unanswered) | | correctCount | number | Total count of correctly answered questions | | currentQuestionIndex | number | Zero-based index of the current question | | username | string | The username received from the parent window during initialization |

State Status

| Property | Type | Description | | ---------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------ | | selectedAnswer | { id: number; content: string } \| null | The currently selected answer object | | isSubmitting | boolean | Indicates whether an answer submission is in progress | | hasSubmitted | boolean | Indicates whether the current question has been submitted and is waiting for the Continue action | | isCompleted | boolean | Indicates whether all questions have been completed |

Methods

| Method | Type | Description | | -------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | handleAnswerSelect | (answer: { id: number; content: string }) => void | Selects an answer for the current question. Only works if not submitting and not already submitted | | updateAnswer | () => Promise<void> | Submits the selected answer to the parent window. Returns a promise that resolves immediately | | handleContinue | () => void | Moves to the next question or marks the quiz as completed if it's the last question. Only works after an answer has been submitted | | finish | () => void | Sends a finish signal to the parent window (typically used to resize iframe) |

Usage Examples

Complete Quiz Flow Example

import { useGameAPI } from "usegamigameapi";

function GameQuiz() {
  const {
    quiz,
    username,
    selectedAnswer,
    currentResult,
    answers,
    correctCount,
    currentQuestionIndex,
    isSubmitting,
    hasSubmitted,
    isCompleted,
    handleAnswerSelect,
    updateAnswer,
    handleContinue,
    finish,
  } = useGameAPI({
    onAnswerCorrect: ({ currentQuestionIndex, correctAnswerId }) => {
      // Show success animation or confetti
      console.log(`Question ${currentQuestionIndex + 1} answered correctly!`, {
        correctAnswerId,
      });
    },
    onAnswerIncorrect: ({
      currentQuestionIndex,
      correctAnswerId,
      incorrectAnswerId,
    }) => {
      // Show error feedback
      console.log("Incorrect answer", { correctAnswerId, incorrectAnswerId });
    },
  });

  // Show loading state while waiting for first question
  if (!quiz) {
    return <div>Loading quiz...</div>;
  }

  // Show completion screen
  if (isCompleted) {
    return (
      <div>
        <h2>Quiz Completed!</h2>
        <p>
          You got {correctCount} out of {answers.length} questions correct.
        </p>
        <button onClick={finish}>Finish</button>
      </div>
    );
  }

  return (
    <div>
      {/* Progress bar */}
      <div>
        Question {currentQuestionIndex + 1} of {answers.length + 1}
        <div style={{ display: "flex", gap: "4px" }}>
          {answers.map((answer, idx) => (
            <div
              key={idx}
              style={{
                width: "20px",
                height: "4px",
                backgroundColor:
                  answer === true ? "green" : answer === false ? "red" : "gray",
              }}
            />
          ))}
        </div>
      </div>

      {/* Question */}
      <h3>{quiz.text}</h3>
      {quiz.audioUrl && <audio src={quiz.audioUrl} controls />}

      {/* Answer options */}
      <div>
        {quiz.answers?.map((answer: { id: number; content: string }) => (
          <button
            key={answer.id}
            onClick={() => handleAnswerSelect(answer)}
            disabled={isSubmitting || hasSubmitted}
            style={{
              backgroundColor:
                selectedAnswer?.id === answer.id ? "#007bff" : "#f0f0f0",
              opacity: hasSubmitted ? 0.6 : 1,
            }}>
            {answer.content}
          </button>
        ))}
      </div>

      {/* Submit button */}
      {!hasSubmitted && (
        <button
          onClick={updateAnswer}
          disabled={!selectedAnswer || isSubmitting}>
          Submit Answer
        </button>
      )}

      {/* Result display and Continue button */}
      {hasSubmitted && currentResult && (
        <div>
          <p>{currentResult.isCorrect ? "✓ Correct!" : "✗ Incorrect"}</p>
          {currentResult.explanation && <p>{currentResult.explanation}</p>}
          <button onClick={handleContinue}>
            {currentResult.isLastQuestion ? "Finish Quiz" : "Next Question"}
          </button>
        </div>
      )}

      {/* Score */}
      <div>Score: {correctCount}</div>
    </div>
  );
}

Minimal Example

import { useGameAPI } from "usegamigameapi";

function SimpleQuiz() {
  const {
    quiz,
    handleAnswerSelect,
    updateAnswer,
    handleContinue,
    selectedAnswer,
    hasSubmitted,
  } = useGameAPI();

  return (
    <div>
      <h2>{quiz?.text}</h2>
      {quiz?.answers?.map((answer) => (
        <button
          key={answer.id}
          onClick={() => handleAnswerSelect(answer)}
          disabled={hasSubmitted}>
          {answer.content}
        </button>
      ))}
      {!hasSubmitted && (
        <button onClick={updateAnswer} disabled={!selectedAnswer}>
          Submit
        </button>
      )}
      {hasSubmitted && <button onClick={handleContinue}>Continue</button>}
    </div>
  );
}

Architecture

This hook uses a bridge pattern to facilitate communication between a parent window (Main Web) and a child iframe (Sub Web) through window.postMessage. The communication flow works as follows:

Communication Flow

  1. Initialization: When the hook mounts, it listens for INIT message from the parent window containing game information (including username)
  2. Start Game: After initialization, the hook sends SHOW_LO5 to request the first question
  3. Question Received: Parent responds with RETURN_LO5 containing question data
  4. Answer Submission: User selects an answer and calls updateAnswer(), which sends UPDATE_ANSWER with the answer ID
  5. Result Received: Parent responds with RETURN_UPDATE_ANSWER containing isCorrect, isLastQuestion, correctAnswerId, incorrectAnswerId, and result data
  6. Continue: User calls handleContinue() which either requests the next question (SHOW_LO5) or marks the quiz as completed
  7. Finish: When complete, finish() sends FINISH to signal completion (typically for iframe resizing)

Action Types

The bridge uses the following action constants (defined internally):

  • INIT: Main → Sub - Initialize game with user information (includes username)
  • SHOW_LO5: Sub → Main - Request a question
  • RETURN_LO5: Main → Sub - Send question data
  • UPDATE_ANSWER: Sub → Main - Submit selected answer
  • RETURN_UPDATE_ANSWER: Main → Sub - Return answer result
  • FINISH: Sub → Main - Signal completion

Notes

  • The hook automatically listens for INIT message from the parent window when mounted to receive game initialization data (including username)
  • The hook automatically requests the first question after initialization
  • Answer history is tracked in the answers array, indexed by question number
  • The hook uses both useState and useRef to ensure correct closure behavior across renders
  • All communication is asynchronous through postMessage events
  • Make sure your parent window is set up to send INIT message with game data and handle the corresponding postMessage events

License

See package.json for license information.