Getting Started
Quick start guide for using FoundryKit hooks
Getting Started
Installation
Install the hooks package using your preferred package manager:
# Using pnpm (recommended)
pnpm add @foundrykit/hooks
# Using npm
npm install @foundrykit/hooks
# Using yarn
yarn add @foundrykit/hooks
Prerequisites
Before using FoundryKit hooks, ensure you have:
- React 19+ - For React hooks functionality
- TypeScript - For type safety (recommended)
Basic Usage
Import and use hooks in your components:
import { useLocalStorage, useDebouncedValue, useMediaQuery } from '@foundrykit/hooks'
import { useState } from 'react'
function MyComponent() {
// Store data in localStorage
const [theme, setTheme] = useLocalStorage('theme', 'light')
// Debounce search input
const [searchQuery, setSearchQuery] = useState('')
const debouncedQuery = useDebouncedValue(searchQuery, 300)
// Respond to media queries
const isMobile = useMediaQuery('(max-width: 768px)')
return (
<div>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Current theme: {theme}
</button>
<input
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search..."
/>
{isMobile ? (
<p>Mobile view</p>
) : (
<p>Desktop view</p>
)}
</div>
)
}
Package Structure
The hooks package is organized as follows:
package.json
tsconfig.json
README.md
Hook Categories
State Management
- useLocalStorage - Persist state in localStorage
- useDebouncedValue - Debounce values and callbacks
UI Interactions
- useClickOutside - Detect clicks outside an element
- useMediaQuery - React to media query changes
Quick Examples
Form with Debounced Search
import { useDebouncedValue } from '@foundrykit/hooks'
import { useState, useEffect } from 'react'
function SearchForm() {
const [query, setQuery] = useState('')
const debouncedQuery = useDebouncedValue(query, 500)
useEffect(() => {
if (debouncedQuery) {
// Perform search with debounced query
performSearch(debouncedQuery)
}
}, [debouncedQuery])
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
)
}
Theme Toggle with Persistence
import { useLocalStorage } from '@foundrykit/hooks'
function ThemeToggle() {
const [theme, setTheme] = useLocalStorage('theme', 'light')
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light')
}
return (
<button onClick={toggleTheme}>
Switch to {theme === 'light' ? 'dark' : 'light'} mode
</button>
)
}
Responsive Component
import { useMediaQuery } from '@foundrykit/hooks'
function ResponsiveComponent() {
const isMobile = useMediaQuery('(max-width: 768px)')
const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)')
const isDesktop = useMediaQuery('(min-width: 1025px)')
return (
<div>
{isMobile && <MobileLayout />}
{isTablet && <TabletLayout />}
{isDesktop && <DesktopLayout />}
</div>
)
}
Click Outside Detection
import { useClickOutside } from '@foundrykit/hooks'
import { useRef, useState } from 'react'
function Dropdown() {
const [isOpen, setIsOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
useClickOutside(dropdownRef, () => {
setIsOpen(false)
})
return (
<div ref={dropdownRef} className="relative">
<button onClick={() => setIsOpen(!isOpen)}>
Toggle Dropdown
</button>
{isOpen && (
<div className="absolute top-full left-0 bg-white border rounded shadow">
<div>Dropdown content</div>
</div>
)}
</div>
)
}
TypeScript Support
All hooks include full TypeScript support:
import { useLocalStorage, useDebouncedValue } from '@foundrykit/hooks'
// TypeScript automatically infers types
const [user, setUser] = useLocalStorage<User>('user', {
id: 1,
name: 'John Doe',
email: 'john@example.com'
})
const [searchTerm, setSearchTerm] = useState('')
const debouncedSearchTerm = useDebouncedValue<string>(searchTerm, 300)
Custom Hook Patterns
Combining Multiple Hooks
import { useLocalStorage, useDebouncedValue, useMediaQuery } from '@foundrykit/hooks'
import { useState } from 'react'
function useSearchWithPersistence() {
const [searchQuery, setSearchQuery] = useLocalStorage('searchQuery', '')
const debouncedQuery = useDebouncedValue(searchQuery, 300)
const isMobile = useMediaQuery('(max-width: 768px)')
return {
searchQuery,
setSearchQuery,
debouncedQuery,
isMobile
}
}
function SearchComponent() {
const { searchQuery, setSearchQuery, debouncedQuery, isMobile } = useSearchWithPersistence()
return (
<div>
<input
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder={isMobile ? "Search..." : "Search for anything..."}
/>
<p>Searching for: {debouncedQuery}</p>
</div>
)
}
Hook Composition
import { useLocalStorage, useDebouncedValue } from '@foundrykit/hooks'
import { useState, useEffect } from 'react'
function usePersistentSearch() {
const [query, setQuery] = useLocalStorage('searchQuery', '')
const debouncedQuery = useDebouncedValue(query, 500)
const [results, setResults] = useState([])
const [isLoading, setIsLoading] = useState(false)
useEffect(() => {
if (debouncedQuery) {
setIsLoading(true)
performSearch(debouncedQuery)
.then(setResults)
.finally(() => setIsLoading(false))
} else {
setResults([])
}
}, [debouncedQuery])
return {
query,
setQuery,
debouncedQuery,
results,
isLoading
}
}
Error Handling
Safe Hook Usage
import { useLocalStorage } from '@foundrykit/hooks'
import { useState, useEffect } from 'react'
function SafeLocalStorage() {
const [hasLocalStorage, setHasLocalStorage] = useState(true)
const [theme, setTheme] = useLocalStorage('theme', 'light')
useEffect(() => {
try {
// Test localStorage availability
localStorage.setItem('test', 'test')
localStorage.removeItem('test')
} catch (error) {
setHasLocalStorage(false)
console.warn('localStorage not available:', error)
}
}, [])
if (!hasLocalStorage) {
// Fallback to regular state
const [fallbackTheme, setFallbackTheme] = useState('light')
return (
<button onClick={() => setFallbackTheme(fallbackTheme === 'light' ? 'dark' : 'light')}>
Theme: {fallbackTheme}
</button>
)
}
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Theme: {theme}
</button>
)
}
Performance Considerations
Memoization with Hooks
import { useDebouncedValue } from '@foundrykit/hooks'
import { useState, useMemo, useCallback } from 'react'
function OptimizedSearch() {
const [query, setQuery] = useState('')
const debouncedQuery = useDebouncedValue(query, 300)
// Memoize expensive operations
const filteredResults = useMemo(() => {
return performExpensiveFilter(debouncedQuery)
}, [debouncedQuery])
// Memoize callbacks
const handleSearch = useCallback((searchQuery: string) => {
setQuery(searchQuery)
}, [])
return (
<div>
<input
value={query}
onChange={(e) => handleSearch(e.target.value)}
placeholder="Search..."
/>
<div>
{filteredResults.map(result => (
<div key={result.id}>{result.name}</div>
))}
</div>
</div>
)
}
Next Steps
- Explore the hook reference to see all available hooks
- Learn about custom hook patterns for advanced usage
- Check out performance optimization techniques
- Review best practices for optimal usage