Claude Code Plugins

Community-maintained marketplace

Feedback

Hook creation and management system for React, Vue, and other frameworks with automated hook generation, testing, and documentation.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name hookify
description Hook creation and management system for React, Vue, and other frameworks with automated hook generation, testing, and documentation.
license MIT

Hook Creation & Management System

Overview

Comprehensive hook development toolkit providing automated hook generation, testing utilities, documentation generation, and management for React, Vue, and other modern frontend frameworks.

Quick Start

Installation

npm install -g @hookify/cli
# or
npx @hookify/cli init

Initialize Hook Project

# Initialize in existing project
hookify init

# Create new hook library
hookify create my-hooks --template=react

# Add to existing project
hookify add --project=my-react-app --framework=react

Hook Generation

React Hooks

# Generate custom hook
hookify generate useUserData --framework=react

# Generate with dependencies
hookify generate useApi --framework=react --deps=useState,useEffect

# Generate with TypeScript
hookify generate useLocalStorage --framework=react --typescript --generic

React Hook Template

// hooks/useApi.ts
import { useState, useEffect, useCallback } from 'react';

interface UseApiOptions<T> {
  immediate?: boolean;
  onSuccess?: (data: T) => void;
  onError?: (error: Error) => void;
}

interface UseApiReturn<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
  execute: () => Promise<void>;
  reset: () => void;
}

export function useApi<T>(
  url: string,
  options: UseApiOptions<T> = {}
): UseApiReturn<T> {
  const { immediate = true, onSuccess, onError } = options;
  
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const execute = useCallback(async () => {
    try {
      setLoading(true);
      setError(null);
      
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const result = await response.json();
      setData(result);
      onSuccess?.(result);
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error');
      setError(error);
      onError?.(error);
    } finally {
      setLoading(false);
    }
  }, [url, onSuccess, onError]);

  const reset = useCallback(() => {
    setData(null);
    setLoading(false);
    setError(null);
  }, []);

  useEffect(() => {
    if (immediate) {
      execute();
    }
  }, [immediate, execute]);

  return { data, loading, error, execute, reset };
}

Vue Composition API

# Generate Vue composable
hookify generate useUserData --framework=vue

# Generate with reactivity
hookify generate useCounter --framework=vue --reactive

# Generate with TypeScript
hookify generate useLocalStorage --framework=vue --typescript

Vue Composable Template

// composables/useApi.ts
import { ref, computed, watch } from 'vue';

interface UseApiOptions<T> {
  immediate?: boolean;
  onSuccess?: (data: T) => void;
  onError?: (error: Error) => void;
}

export function useApi<T>(
  url: string,
  options: UseApiOptions<T> = {}
) {
  const { immediate = true, onSuccess, onError } = options;
  
  const data = ref<T | null>(null);
  const loading = ref(false);
  const error = ref<Error | null>(null);

  const execute = async () => {
    try {
      loading.value = true;
      error.value = null;
      
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const result = await response.json();
      data.value = result;
      onSuccess?.(result);
    } catch (err) {
      const errorValue = err instanceof Error ? err : new Error('Unknown error');
      error.value = errorValue;
      onError?.(errorValue);
    } finally {
      loading.value = false;
    }
  };

  const reset = () => {
    data.value = null;
    loading.value = false;
    error.value = null;
  };

  // Computed properties
  const isIdle = computed(() => !loading.value && !error.value && data.value === null);
  const isSuccess = computed(() => !loading.value && !error.value && data.value !== null);
  const isError = computed(() => !loading.value && error.value !== null);

  // Auto-execute if immediate
  if (immediate) {
    execute();
  }

  return {
    data,
    loading: readonly(loading),
    error: readonly(error),
    execute,
    reset,
    isIdle,
    isSuccess,
    isError
  };
}

Svelte Hooks

# Generate Svelte store
hookify generate useUserData --framework=svelte

# Generate writable store
hookify generate useCounter --framework=svelte --type=writable

# Generate derived store
hookify generate useFilteredData --framework=svelte --type=derived

Svelte Store Template

// stores/useApi.ts
import { writable, derived } from 'svelte/store';

interface ApiState<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
}

function createApiStore<T>(url: string) {
  const { subscribe, set, update } = writable<ApiState<T>>({
    data: null,
    loading: false,
    error: null
  });

  const execute = async () => {
    update(state => ({ ...state, loading: true, error: null }));
    
    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const result = await response.json();
      set({ data: result, loading: false, error: null });
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error');
      set({ data: null, loading: false, error });
    }
  };

  const reset = () => {
    set({ data: null, loading: false, error: null });
  };

  // Derived stores
  const isIdle = derived(store, $store => !$store.loading && !$store.error && $store.data === null);
  const isSuccess = derived(store, $store => !$store.loading && !$store.error && $store.data !== null);
  const isError = derived(store, $store => !$store.loading && $store.error !== null);

  return {
    subscribe,
    execute,
    reset,
    isIdle,
    isSuccess,
    isError
  };
}

export const useApi = createApiStore;

Hook Templates

Common Hook Patterns

# Generate data fetching hook
hookify generate useFetch --template=data-fetching

# Generate form handling hook
hookify generate useForm --template=form-handling

# Generate authentication hook
hookify generate useAuth --template=authentication

# Generate local storage hook
hookify generate useLocalStorage --template=storage

Data Fetching Template

// templates/data-fetching.hook.ts
export const dataFetchingTemplate = `
import { useState, useEffect, useCallback, useRef } from 'react';

interface Use{{Name}}Options<T> {
  immediate?: boolean;
  cache?: boolean;
  cacheTime?: number;
  retry?: number;
  retryDelay?: number;
  onSuccess?: (data: T) => void;
  onError?: (error: Error) => void;
}

interface Use{{Name}}Return<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
  execute: () => Promise<void>;
  refetch: () => Promise<void>;
  reset: () => void;
}

export function use{{Name}}<T>(
  fetcher: () => Promise<T>,
  options: Use{{Name}}Options<T> = {}
): Use{{Name}}Return<T> {
  const { 
    immediate = true, 
    cache = false, 
    cacheTime = 300000, // 5 minutes
    retry = 3,
    retryDelay = 1000,
    onSuccess,
    onError 
  } = options;
  
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  
  const cacheRef = useRef<Map<string, { data: T; timestamp: number }>>(new Map());
  const retryCountRef = useRef(0);

  const execute = useCallback(async () => {
    try {
      setLoading(true);
      setError(null);
      
      // Check cache
      if (cache) {
        const cacheKey = fetcher.toString();
        const cached = cacheRef.current.get(cacheKey);
        
        if (cached && Date.now() - cached.timestamp < cacheTime) {
          setData(cached.data);
          onSuccess?.(cached.data);
          return;
        }
      }
      
      const result = await fetcher();
      setData(result);
      
      // Update cache
      if (cache) {
        const cacheKey = fetcher.toString();
        cacheRef.current.set(cacheKey, { data: result, timestamp: Date.now() });
      }
      
      retryCountRef.current = 0;
      onSuccess?.(result);
    } catch (err) {
      const error = err instanceof Error ? err : new Error('Unknown error');
      
      // Retry logic
      if (retryCountRef.current < retry) {
        retryCountRef.current++;
        setTimeout(execute, retryDelay * retryCountRef.current);
        return;
      }
      
      setError(error);
      onError?.(error);
    } finally {
      setLoading(false);
    }
  }, [fetcher, cache, cacheTime, retry, retryDelay, onSuccess, onError]);

  const refetch = useCallback(() => {
    retryCountRef.current = 0;
    return execute();
  }, [execute]);

  const reset = useCallback(() => {
    setData(null);
    setLoading(false);
    setError(null);
    retryCountRef.current = 0;
  }, []);

  useEffect(() => {
    if (immediate) {
      execute();
    }
  }, [immediate, execute]);

  return { data, loading, error, execute, refetch, reset };
}
`;

Form Handling Template

// templates/form-handling.hook.ts
export const formHandlingTemplate = `
import { useState, useCallback, useEffect } from 'react';

interface FieldValidation {
  required?: boolean;
  minLength?: number;
  maxLength?: number;
  pattern?: RegExp;
  custom?: (value: any) => string | null;
}

interface FormField<T = any> {
  value: T;
  error: string | null;
  touched: boolean;
  validation?: FieldValidation;
}

interface UseFormOptions<T> {
  initialValues: T;
  validation?: Partial<Record<keyof T, FieldValidation>>;
  onSubmit?: (values: T) => void | Promise<void>;
  validateOnChange?: boolean;
}

interface UseFormReturn<T> {
  values: T;
  errors: Partial<Record<keyof T, string | null>>;
  touched: Partial<Record<keyof T, boolean>>;
  isValid: boolean;
  isDirty: boolean;
  isSubmitting: boolean;
  setValue: <K extends keyof T>(field: K, value: T[K]) => void;
  setError: <K extends keyof T>(field: K, error: string | null) => void;
  setTouched: <K extends keyof T>(field: K, touched: boolean) => void;
  validateField: <K extends keyof T>(field: K) => string | null;
  validateForm: () => boolean;
  handleSubmit: () => Promise<void>;
  resetForm: () => void;
  resetField: <K extends keyof T>(field: K) => void;
}

export function useForm<T extends Record<string, any>>(
  options: UseFormOptions<T>
): UseFormReturn<T> {
  const { initialValues, validation = {}, onSubmit, validateOnChange = true } = options;
  
  const [values, setValues] = useState<T>(initialValues);
  const [errors, setErrors] = useState<Partial<Record<keyof T, string | null>>>({});
  const [touched, setTouched] = useState<Partial<Record<keyof T, boolean>>>({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  const validateField = useCallback(<K extends keyof T>(field: K): string | null => {
    const value = values[field];
    const fieldValidation = validation[field];
    
    if (!fieldValidation) return null;
    
    // Required validation
    if (fieldValidation.required && (!value || value === '')) {
      return 'This field is required';
    }
    
    // Length validation
    if (typeof value === 'string') {
      if (fieldValidation.minLength && value.length < fieldValidation.minLength) {
        return \`Minimum length is \${fieldValidation.minLength} characters\`;
      }
      if (fieldValidation.maxLength && value.length > fieldValidation.maxLength) {
        return \`Maximum length is \${fieldValidation.maxLength} characters\`;
      }
    }
    
    // Pattern validation
    if (fieldValidation.pattern && typeof value === 'string') {
      if (!fieldValidation.pattern.test(value)) {
        return 'Invalid format';
      }
    }
    
    // Custom validation
    if (fieldValidation.custom) {
      return fieldValidation.custom(value);
    }
    
    return null;
  }, [values, validation]);

  const validateForm = useCallback((): boolean => {
    const newErrors: Partial<Record<keyof T, string | null>> = {};
    let isValid = true;
    
    Object.keys(validation).forEach((field) => {
      const error = validateField(field as keyof T);
      newErrors[field as keyof T] = error;
      if (error) isValid = false;
    });
    
    setErrors(newErrors);
    return isValid;
  }, [validateField, validation]);

  const setValue = useCallback(<K extends keyof T>(field: K, value: T[K]) => {
    setValues(prev => ({ ...prev, [field]: value }));
    
    if (validateOnChange) {
      const error = validateField(field);
      setErrors(prev => ({ ...prev, [field]: error }));
    }
  }, [validateField, validateOnChange]);

  const setError = useCallback(<K extends keyof T>(field: K, error: string | null) => {
    setErrors(prev => ({ ...prev, [field]: error }));
  }, []);

  const setTouched = useCallback(<K extends keyof T>(field: K, touchedValue: boolean) => {
    setTouched(prev => ({ ...prev, [field]: touchedValue }));
  }, []);

  const handleSubmit = useCallback(async () => {
    // Validate all fields
    const isValid = validateForm();
    
    if (!isValid) return;
    
    setIsSubmitting(true);
    
    try {
      await onSubmit?.(values);
    } catch (error) {
      console.error('Form submission error:', error);
    } finally {
      setIsSubmitting(false);
    }
  }, [validateForm, onSubmit, values]);

  const resetForm = useCallback(() => {
    setValues(initialValues);
    setErrors({});
    setTouched({});
    setIsSubmitting(false);
  }, [initialValues]);

  const resetField = useCallback(<K extends keyof T>(field: K) => {
    setValues(prev => ({ ...prev, [field]: initialValues[field] }));
    setErrors(prev => ({ ...prev, [field]: null }));
    setTouched(prev => ({ ...prev, [field]: false }));
  }, [initialValues]);

  // Computed values
  const isValid = Object.values(errors).every(error => !error);
  const isDirty = Object.keys(touched).some(key => touched[key as keyof T]);

  return {
    values,
    errors,
    touched,
    isValid,
    isDirty,
    isSubmitting,
    setValue,
    setError,
    setTouched,
    validateField,
    validateForm,
    handleSubmit,
    resetForm,
    resetField
  };
}
`;

Hook Testing

Test Generation

# Generate hook tests
hookify test generate useApi --framework=react-testing-library

# Generate test with custom scenarios
hookify test generate useLocalStorage --scenarios=basic,error,edge-cases

# Generate performance tests
hookify test generate useApi --performance --memory-leaks

Hook Test Template

// test/hooks/useApi.test.ts
import { renderHook, act, waitFor } from '@testing-library/react';
import { useApi } from '../hooks/useApi';

// Mock fetch
global.fetch = jest.fn();

describe('useApi', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test('should initialize with loading state', () => {
    const { result } = renderHook(() => useApi('https://api.example.com/data'));
    
    expect(result.current.loading).toBe(true);
    expect(result.current.data).toBe(null);
    expect(result.current.error).toBe(null);
  });

  test('should fetch data successfully', async () => {
    const mockData = { id: 1, name: 'Test Data' };
    (fetch as jest.Mock).mockResolvedValueOnce({
      ok: true,
      json: async () => mockData
    });

    const { result } = renderHook(() => useApi('https://api.example.com/data'));

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
      expect(result.current.data).toEqual(mockData);
      expect(result.current.error).toBe(null);
    });

    expect(fetch).toHaveBeenCalledWith('https://api.example.com/data');
  });

  test('should handle fetch error', async () => {
    const mockError = new Error('Network error');
    (fetch as jest.Mock).mockRejectedValueOnce(mockError);

    const onError = jest.fn();
    const { result } = renderHook(() => 
      useApi('https://api.example.com/data', { onError })
    );

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
      expect(result.current.data).toBe(null);
      expect(result.current.error).toEqual(mockError);
    });

    expect(onError).toHaveBeenCalledWith(mockError);
  });

  test('should not fetch immediately when immediate is false', () => {
    const { result } = renderHook(() => 
      useApi('https://api.example.com/data', { immediate: false })
    );
    
    expect(result.current.loading).toBe(false);
    expect(fetch).not.toHaveBeenCalled();
  });

  test('should execute manual refetch', async () => {
    const mockData = { id: 1, name: 'Test Data' };
    (fetch as jest.Mock).mockResolvedValueOnce({
      ok: true,
      json: async () => mockData
    });

    const { result } = renderHook(() => 
      useApi('https://api.example.com/data', { immediate: false })
    );

    act(() => {
      result.current.execute();
    });

    await waitFor(() => {
      expect(result.current.data).toEqual(mockData);
    });

    expect(fetch).toHaveBeenCalledTimes(1);
  });

  test('should reset state', () => {
    const { result } = renderHook(() => 
      useApi('https://api.example.com/data', { immediate: false })
    );

    // Simulate some state change
    act(() => {
      result.current.reset();
    });

    expect(result.current.data).toBe(null);
    expect(result.current.loading).toBe(false);
    expect(result.current.error).toBe(null);
  });
});

Performance Testing

// test/performance/useApi.performance.test.ts
import { renderHook, act } from '@testing-library/react';
import { useApi } from '../hooks/useApi';

describe('useApi Performance', () => {
  test('should not cause memory leaks', () => {
    const { unmount } = renderHook(() => useApi('https://api.example.com/data'));
    
    // Force garbage collection if available
    if (global.gc) {
      global.gc();
    }
    
    // Unmount hook
    unmount();
    
    // Check for memory leaks (implementation depends on your setup)
    // This is a placeholder for actual memory leak detection
    expect(true).toBe(true);
  });

  test('should handle rapid state updates efficiently', async () => {
    const startTime = performance.now();
    
    const { result } = renderHook(() => useApi('https://api.example.com/data'));
    
    // Simulate rapid calls
    for (let i = 0; i < 100; i++) {
      act(() => {
        result.current.execute();
      });
    }
    
    const endTime = performance.now();
    const duration = endTime - startTime;
    
    // Should complete within reasonable time (adjust threshold as needed)
    expect(duration).toBeLessThan(1000); // 1 second
  });
});

Hook Documentation

Auto-Documentation

# Generate hook documentation
hookify docs generate useApi --format=markdown

# Generate Storybook stories
hookify docs storybook useApi --framework=react

# Generate API reference
hookify docs api --output=./docs/hooks

Documentation Template

# useApi

A custom hook for handling API calls with loading states, error handling, and caching capabilities.

## Usage

\`\`\`typescript
import { useApi } from './hooks/useApi';

interface User {
  id: number;
  name: string;
  email: string;
}

function UserProfile({ userId }: { userId: number }) {
  const { data: user, loading, error, execute } = useApi<User>(
    \`https://api.example.com/users/\${userId}\`
  );

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  if (!user) return <div>No user found</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
      <button onClick={execute}>Refresh</button>
    </div>
  );
}
\`\`\`

## API

### Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| url | \`string\` | - | The API endpoint URL |
| options | \`UseApiOptions<T>\` | \`{}\` | Configuration options |

### Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| immediate | \`boolean\` | \`true\` | Whether to fetch data immediately |
| onSuccess | \`(data: T) => void\` | - | Callback called on successful fetch |
| onError | \`(error: Error) => void\` | - | Callback called on error |

### Return Value

| Property | Type | Description |
|----------|------|-------------|
| data | \`T | null\` | The fetched data |
| loading | \`boolean\` | Whether the request is in progress |
| error | \`Error | null\` | Any error that occurred |
| execute | \`() => Promise<void>\` | Function to manually trigger the request |
| reset | \`() => void\` | Function to reset the hook state |

## Examples

### Basic Usage

\`\`\`typescript
const { data, loading, error } = useApi('https://api.example.com/data');
\`\`\`

### With Error Handling

\`\`\`typescript
const { data, loading, error } = useApi('https://api.example.com/data', {
  onError: (error) => console.error('API Error:', error)
});
\`\`\`

### Manual Execution

\`\`\`typescript
const { data, loading, error, execute } = useApi(
  'https://api.example.com/data',
  { immediate: false }
);

// Trigger request manually
const handleRefresh = () => {
  execute();
};
\`\`\`

## TypeScript Support

This hook is fully typed with TypeScript:

\`\`\`typescript
interface ApiResponse {
  id: number;
  name: string;
}

const { data } = useApi<ApiResponse>('https://api.example.com/data');
// data is typed as ApiResponse | null
\`\`\`

## Dependencies

- React 16.8+ (for hooks support)
- TypeScript 4.0+ (for type support)

## Related Hooks

- \`useFetch\` - Simpler data fetching hook
- \`useLocalStorage\` - Local storage synchronization
- \`useDebounce\` - Debounced values

Hook Registry

Hook Management

# List all hooks
hookify list

# Search hooks
hookify search --keyword=api

# Get hook info
hookify info useApi

# Install hook from registry
hookify install useAuth --registry=@company/hooks

Hook Registry Configuration

// hookify.config.js
module.exports = {
  registry: {
    default: 'https://registry.hookify.dev',
    private: 'https://hooks.company.com',
    local: './hooks'
  },
  
  hooks: {
    // Local hooks
    './hooks': {
      pattern: '**/*.hook.{js,ts}',
      autoRegister: true
    },
    
    // Registry hooks
    '@company/hooks': {
      version: '^1.0.0',
      autoUpdate: true
    }
  },
  
  validation: {
    typescript: true,
    tests: true,
    documentation: true,
    performance: true
  },
  
  publishing: {
    registry: 'https://registry.hookify.dev',
    autoVersion: true,
    changelog: true
  }
};

Hook Publishing

# Publish hook to registry
hookify publish useApi --registry=public

# Publish with version
hookify publish useAuth --version=2.1.0

# Publish to private registry
hookify publish useCompanyData --registry=@company/hooks

Integration

Framework Integration

# React integration
hookify integrate react --typescript=true

# Vue integration
hookify integrate vue --composition-api

# Svelte integration
hookify integrate svelte --typescript=true

Build Integration

// webpack.config.js
const { HookifyPlugin } = require('@hookify/webpack');

module.exports = {
  plugins: [
    new HookifyPlugin({
      hooks: './src/hooks',
      output: './dist/hooks',
      optimization: {
        treeShaking: true,
        minify: true,
        bundleAnalysis: true
      }
    })
  ]
};

API Reference

Core Classes

HookGenerator

import { HookGenerator } from '@hookify/core';

const generator = new HookGenerator({
  framework: 'react',
  typescript: true,
  testing: true
});

const hook = await generator.generate('useApi', {
  template: 'data-fetching',
  dependencies: ['useState', 'useEffect']
});

HookTester

import { HookTester } from '@hookify/testing';

const tester = new HookTester({
  framework: 'react-testing-library',
  coverage: true
});

const testResults = await tester.test('useApi');

HookRegistry

import { HookRegistry } from '@hookify/registry';

const registry = new HookRegistry({
  endpoint: 'https://registry.hookify.dev'
});

const hooks = await registry.search('api');
const hook = await registry.get('useApi');

Contributing

  1. Fork repository
  2. Create hook feature branch
  3. Add comprehensive tests
  4. Generate documentation
  5. Submit pull request

License

MIT License - see LICENSE file for details.