How Tool Data Is Structured in FckSignups: A Complete Guide to the JSON Schema

FckSignups stores all tool metadata in a single JSON file (tools.json) following a strict TypeScript schema with two top-level arrays—categories and tools—defined in src/types/index.ts.

The FckSignups repository organizes its entire catalog of sign-up-free tools using a flat JSON structure that prioritizes type safety and runtime flexibility. All public tool information lives in the root-level tools.json file, while TypeScript interfaces in src/types/index.ts enforce compile-time validation across the React application.

The Core JSON Architecture

At the highest level, tools.json contains two parallel arrays that drive the entire UI:

{
  "categories": [ ... ],
  "tools": [ ... ]
}
  • categories – Defines the navigation taxonomy and filtering options available in the interface.
  • tools – Contains individual tool metadata including URLs, descriptions, GitHub statistics, and UI placement directives.

This bifurcated structure allows the frontend to render category navigation independently from the tool grid, enabling efficient filtering without redundant data duplication.

TypeScript Schema Definitions

The contract between the JSON source and the React components is enforced through interfaces located in src/types/index.ts.

Category Interface

Each category object follows the Category interface:

export interface Category {
  id: string;          // e.g. "productivity"
  name: string;        // human-readable label
  icon: string;        // emoji or UI icon
  description: string; // short blurb
}

The id field serves as the foreign key that links tools to their respective categories.

Tool Interface

Individual tool entries conform to the Tool interface, which includes optional fields for GitHub metadata and editorial status:

export interface Tool {
  id: string;                     // stable identifier (used for URLs, keys)
  name: string;                   // display name
  description: string;            // short marketing copy
  url: string;                    // direct link to the app
  category: string;               // matches a Category.id
  tags: string[];                 // free-form keywords for search
  github?: string;                // optional source repo
  license?: string;               // optional license name
  stars?: number;                 // GitHub star count (optional)
  section?: "featured" | "editors-pick" | "meets-criteria"; // UI grouping
  flag?: "new" | "abandoned";     // optional status flag
  addedAt?: string;               // ISO-date string for sorting
  notRecommendedReason?: string; // optional reason to hide from UI
}

The optional chaining (?) on fields like github, stars, and section allows the JSON to remain lean for tools without repository links or those awaiting editorial review.

Category Metadata Structure

Category objects provide the taxonomy that drives the sidebar navigation and filter chips. A typical category entry looks like this:

{
  "id": "design",
  "name": "Design & Graphics",
  "icon": "🎨",
  "description": "Create visuals right in your browser"
}

The UI constructs its category list directly from this array. At runtime, the application automatically injects a special "all" category (if missing) that aggregates every tool regardless of classification, ensuring users can always view the complete catalog.

Tool Entry Field Reference

Each object in the tools array represents a single application or utility. Here is a representative entry for Excalidraw:

{
  "id": "excalidraw",
  "name": "Excalidraw",
  "description": "Virtual whiteboard for sketching hand-drawn like diagrams",
  "url": "https://excalidraw.com",
  "category": "design",
  "tags": ["whiteboard", "diagrams", "sketch", "collaboration"],
  "github": "https://github.com/excalidraw/excalidraw",
  "license": "MIT",
  "stars": 131341,
  "addedAt": "2026-05-07",
  "section": "featured"
}

Required vs. Optional Fields

Field Type Purpose
id string Stable React key and URL slug for deep-linking
name string Display label rendered in tool cards
description string Marketing copy shown beneath the tool name
url string Direct external link users click to access the tool
category string Foreign key matching a Category.id; drives filtering logic
tags string[] Searchable keywords used by the tokenization algorithm
github string (optional) Repository URL for displaying source availability
license string (optional) SPDX identifier or license name
stars number (optional) GitHub star count displayed as social proof
section enum (optional) Editorial placement: "featured", "editors-pick", or "meets-criteria"
flag enum (optional) Status indicator: "new" or "abandoned"
addedAt ISO date (optional) Temporal marker for "new" badge logic and sorting
notRecommendedReason string (optional) Exclusion note; if present, hides tool from main listings

Runtime Loading and Fallback Strategy

The src/hooks/useTools.ts file orchestrates data fetching, caching, and normalization through the useTools React hook.

Environment-Aware Fetching

The hook implements a tiered loading strategy:

  1. Development mode – Loads from DEV_JSON_URL (local tools.json)
  2. Production – Fetches from PROD_JSON_URL (raw GitHub URL for the repository)
  3. Failure fallback – Returns FALLBACK_DATA from src/constants/fallbackData.ts if both network requests fail

Data Normalization Pipeline

Once loaded, the hook processes the raw JSON through three transformations:

  1. Hydration – Guarantees an "all" category exists for aggregate viewing
  2. Sectionization – Normalizes tools into ToolSections based on the section field for grid layout
  3. Tokenization – Pre-processes tags and description fields for fuzzy search matching via tokenize() and matchScore() utilities

Practical Implementation Examples

Accessing Tools in Components

Consume the data layer using the useTools hook:

import { useTools } from "../hooks/useTools";

export function ToolCatalog() {
  const { tools, loadStatus, errorMessage } = useTools();

  if (loadStatus === "loading") return <p>Loading…</p>;
  if (loadStatus === "error") return <p>{errorMessage}</p>;

  return (
    <ul>
      {tools.map((t) => (
        <li key={t.id}>
          <a href={t.url} target="_blank" rel="noopener">
            {t.name}
          </a>
          {" "}
          – {t.description}
        </li>
      ))}
    </ul>
  );
}

The hook automatically merges remote JSON, fallback data, and injects the synthetic "all" category, returning a ready-to-render array.

Implementing Search and Category Filters

The useTools hook exposes state setters and memoized filtered results for building interactive UIs:

import { useTools } from "../hooks/useTools";

export function SearchableToolList() {
  const {
    filteredTools,
    categories,
    setActiveCategory,
    setSearchQuery,
    activeCategory,
    searchQuery,
  } = useTools();

  return (
    <>
      <select
        value={activeCategory}
        onChange={(e) => setActiveCategory(e.target.value)}
      >
        {categories.map((c) => (
          <option key={c.id} value={c.id}>
            {c.icon} {c.name}
          </option>
        ))}
      </select>

      <input
        type="search"
        placeholder="Search tools…"
        value={searchQuery}
        onChange={(e) => setSearchQuery(e.target.value)}
      />

      <ul>
        {filteredTools.map((t) => (
          <li key={t.id}>
            <a href={t.url}>{t.name}</a>
            {t.stars && <span>★{t.stars}</span>}
          </li>
        ))}
      </ul>
    </>
  );
}

The filteredTools array automatically applies both the active category filter and the search-keyword scoring logic defined in useTools.ts.

Adding Static Fallback Data

To ensure a tool appears even when GitHub Pages or the raw JSON endpoint is unreachable, append entries to FALLBACK_DATA in src/constants/fallbackData.ts:

import type { ToolsData } from "../types";

export const FALLBACK_DATA: ToolsData = {
  categories: [
    { id: "utilities", name: "Utilities", icon: "🛠️", description: "Helpful tools" }
  ],
  tools: [
    {
      id: "my-awesome-tool",
      name: "My Awesome Tool",
      description: "A neat utility that requires no signup.",
      url: "https://example.com",
      category: "utilities",
      tags: ["awesome", "demo"],
      github: "https://github.com/example/tool",
      license: "MIT",
      stars: 123,
      addedAt: "2026-09-01",
      section: "featured",
    },
  ],
};

After rebuilding, this entry renders immediately without network dependencies.

Summary

  • FckSignups maintains all tool metadata in a flat tools.json structure with categories and tools arrays.
  • TypeScript interfaces in src/types/index.ts strictly define the Tool and Category schemas, enforcing type safety across the React codebase.
  • Tool entries require id, name, description, url, category, and tags, with optional fields for GitHub metadata (stars, license), editorial placement (section), and status flags (flag).
  • Runtime loading employs a resilient strategy via useTools.ts, falling back to hardcoded FALLBACK_DATA when network requests fail.
  • Automatic normalization adds an "all" category and pre-computes search tokens for client-side filtering.

Frequently Asked Questions

Where is the tool data physically stored in the FckSignups repository?

All tool metadata resides in the root-level tools.json file, which is served directly from the repository and fetched at runtime by the useTools hook. TypeScript definitions that validate this structure are located in src/types/index.ts.

What fields are required when adding a new tool to the catalog?

Every tool object must include id (unique string), name (display label), description (UI copy), url (external link), category (matching a valid category ID), and tags (array of search keywords). Optional fields such as github, stars, license, section, and addedAt enhance the display but are not required for the entry to render.

How does FckSignups handle data loading failures or offline scenarios?

The application implements a three-tier fallback system in useTools.ts: it attempts to load local JSON in development, falls back to the raw GitHub URL in production, and finally returns FALLBACK_DATA from src/constants/fallbackData.ts if both network requests fail. This ensures the UI remains functional even without connectivity.

What is the purpose of the section field in tool entries?

The optional section field controls editorial placement within the UI grid, accepting one of three string values: "featured" (highlighted prominently), "editors-pick" (curated selections), or "meets-criteria" (standard listings). Tools without this field default to the standard grid layout without special visual distinction.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →