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:
- Development mode – Loads from
DEV_JSON_URL(localtools.json) - Production – Fetches from
PROD_JSON_URL(raw GitHub URL for the repository) - Failure fallback – Returns
FALLBACK_DATAfrom src/constants/fallbackData.ts if both network requests fail
Data Normalization Pipeline
Once loaded, the hook processes the raw JSON through three transformations:
- Hydration – Guarantees an
"all"category exists for aggregate viewing - Sectionization – Normalizes tools into
ToolSectionsbased on thesectionfield for grid layout - Tokenization – Pre-processes
tagsanddescriptionfields for fuzzy search matching viatokenize()andmatchScore()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.jsonstructure withcategoriesandtoolsarrays. - TypeScript interfaces in
src/types/index.tsstrictly define theToolandCategoryschemas, enforcing type safety across the React codebase. - Tool entries require
id,name,description,url,category, andtags, 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 hardcodedFALLBACK_DATAwhen 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →