A comprehensive toolkit providing React components, utilities, and state management solutions for Next.js applications built with the Clutch framework.
npm install @clutch-creator/toolkit
# or
yarn add @clutch-creator/toolkit
# or
bun add @clutch-creator/toolkitThe Clutch Toolkit is designed to provide developers with a set of reusable components and utilities that integrate seamlessly with Next.js applications. It includes state management, SEO optimization, image handling, and various utility functions.
Registers a state to be globally bindable in clutch.
Type Signature:
useRegisterState<T>(name: string, value: T): (newValue: T) => voidExample:
import { useRegisterState } from '@clutch-creator/toolkit';
import { useState } from 'react';
function Counter() {
const [counter, setCounter] = useState(0);
// Register the state so it can be accessed globally in Clutch
useRegisterState<number>('count', counter);
const increment = () => setCounter(prev => prev + 1);
const reset = () => setCounter(0);
return (
<div>
<button onClick={increment}>Increment</button>
<button onClick={reset}>Reset</button>
</div>
);
}Registers actions for components that can be triggered by events.
Type Signature:
useRegisterAction<T extends (...args: unknown[]) => unknown>(
options: TRegisterActionOptions<T>
): void
interface TRegisterActionOptions<T> {
name: string;
action: T;
props?: Record<string, unknown>;
wrapper?: React.FunctionComponent<{
children: React.ReactNode;
[key: string]: unknown;
}>;
styleSelectors?: TStyleSelector[];
}
interface TStyleSelector {
name: string;
value: string;
}Example:
import { useRegisterAction } from '@clutch-creator/toolkit';
import { useState } from 'react';
type TSetToggled = (toggled: boolean) => void;
function TodoItem({ id, text, completed }) {
const [toggled, setToggled] = useState(false);
useRegisterAction<TSetToggled>({
name: 'setToggled',
action: (newValue) => {
setToggled((value) => newValue);
},
props: {
'data-toggled': toggled
},
styleSelectors: [
{ name: 'Toggled', value: '&:[data-toggled=true]' }
]
});
return <div className={completed ? 'completed' : ''}>{text}</div>;
}Registers a handler to be called when the element is selected in clutch.
Type Signature:
useRegisterSelect(
setVisibility: (shouldBeVisible: boolean) => void,
activeTrail?: boolean
): nullExample:
import { useRegisterSelect } from '@clutch-creator/toolkit';
import { useState } from 'react';
function ConditionalContent({ children }) {
const [isVisible, setIsVisible] = useState(true);
useRegisterSelect(
(shouldBeVisible) => setIsVisible(shouldBeVisible),
true // activeTrail - whether it should be called when a child is selected
);
if (!isVisible) return null;
return <div className="conditional-content">{children}</div>;
}Updates URL search parameters while preserving existing ones. Useful for filtering, pagination, and state management through URLs.
Type Signature:
updateUrlSearchParams(
newParams: Record<string, unknown>,
router: NextRouter
): Record<string, unknown>Example:
import { updateUrlSearchParams } from '@clutch-creator/toolkit';
import { useRouter } from 'next/router';
function ProductFilters() {
const router = useRouter();
const handleCategoryFilter = (category: string) => {
updateUrlSearchParams({ category, page: 1 }, router);
// URL: /products?category=electronics&page=1
};
const handlePriceRange = (min: number, max: number) => {
updateUrlSearchParams({
priceMin: min.toString(),
priceMax: max.toString()
}, router);
// URL: /products?category=electronics&page=1&priceMin=100&priceMax=500
};
const clearFilters = () => {
updateUrlSearchParams({
category: null,
priceMin: null,
priceMax: null
}, router);
// URL: /products
};
return (
<div>
<button onClick={() => handleCategoryFilter('electronics')}>
Electronics
</button>
<button onClick={() => handlePriceRange(100, 500)}>
$100-$500
</button>
<button onClick={clearFilters}>Clear Filters</button>
</div>
);
}Registers React components for use in the Clutch visual editor with optional configuration.
Type Signature:
clutchElementConfig(
element: React.FunctionComponent,
config: {
icon?: string;
styleSelectors?: { name?: string; value: string }[];
}
): voidExample:
import { clutchElementConfig } from '@clutch-creator/toolkit';
const Button = ({ children, variant = 'primary', ...props }) => (
<button className={`btn btn-${variant}`} {...props}>
{children}
</button>
);
const Card = ({ title, children, ...props }) => (
<div className="card" {...props}>
<h3 className="card-title">{title}</h3>
<div className="card-content">{children}</div>
</div>
);
// Register components with Clutch editor
clutchElementConfig(Button, {
icon: 'π',
styleSelectors: [
{ name: 'Hover', value: '&:hover' },
{ name: 'Disabled', value: '&:disabled' },
]
});
clutchElementConfig(Card, {
icon: 'π',
styleSelectors: [
{ name: 'Card Hover', value: '&:hover' },
]
});Conditional logging utility with different methods for development and debugging.
Type Signature:
logger: {
log(...args: unknown[]): void; // Only in development
debug(...args: unknown[]): void; // Only when window.CLUTCH_DEBUG = true
warn(...args: unknown[]): void; // In development or server
error(...args: unknown[]): void; // In development or server
}Example:
import { logger } from '@clutch-creator/toolkit';
function DataProcessor({ data }) {
// Always logs in development
logger.log('Processing data:', data);
// Only logs when window.CLUTCH_DEBUG = true
logger.debug('Debug info:', data);
try {
const processed = processData(data);
logger.log('Data processed successfully:', processed);
return processed;
} catch (error) {
logger.error('Error processing data:', error);
logger.warn('Falling back to default data');
return getDefaultData();
}
}
// In browser console to enable debug logging:
// window.CLUTCH_DEBUG = true;Clones React children and applies the given props to each child element. Filters out debug-related props and handles both single children and arrays of children.
Type Signature:
cloneChildren(
children: React.ReactNode,
props: Record<string, unknown>
): React.ReactNodeExample:
import { cloneChildren } from '@clutch-creator/toolkit';
function WrapperComponent({ children, className, onClick }) {
// Clone children and add common props
const clonedChildren = cloneChildren(children, {
className: `child-element ${className}`,
onClick: onClick,
'data-wrapper': true
});
return <div className="wrapper">{clonedChildren}</div>;
}
// Usage
function App() {
return (
<WrapperComponent className="highlighted" onClick={() => console.log('clicked')}>
<button>Button 1</button>
<button>Button 2</button>
</WrapperComponent>
);
}
// Both buttons will receive className="child-element highlighted" and onClick handlerThrown when required environment variables are missing.
Type Signature:
class MissingEnvVariableError extends Error {
constructor(envName: string);
}Example:
import { MissingEnvVariableError } from '@clutch-creator/toolkit';
function initializeApp() {
const apiKey = process.env.NEXT_PUBLIC_API_KEY;
const dbUrl = process.env.DATABASE_URL;
if (!apiKey) {
throw new MissingEnvVariableError('NEXT_PUBLIC_API_KEY');
}
if (!dbUrl) {
throw new MissingEnvVariableError('DATABASE_URL');
}
// Initialize app with environment variables
return { apiKey, dbUrl };
}
// Usage with error handling
try {
const config = initializeApp();
} catch (error) {
if (error instanceof MissingEnvVariableError) {
console.error('Configuration error:', error.message);
// Handle missing environment variable
}
}Thrown when environment variables have invalid values.
Type Signature:
class InvalidEnvVariableError extends Error {
constructor(envName: string);
}Example:
import { InvalidEnvVariableError } from '@clutch-creator/toolkit';
function validateConfig() {
const port = process.env.PORT;
const nodeEnv = process.env.NODE_ENV;
if (port && isNaN(parseInt(port))) {
throw new InvalidEnvVariableError('PORT');
}
if (nodeEnv && !['development', 'production', 'test'].includes(nodeEnv)) {
throw new InvalidEnvVariableError('NODE_ENV');
}
return {
port: port ? parseInt(port) : 3000,
nodeEnv: nodeEnv || 'development',
};
}
// Usage
try {
const config = validateConfig();
} catch (error) {
if (error instanceof InvalidEnvVariableError) {
console.error('Invalid configuration:', error.message);
process.exit(1);
}
}The toolkit exports comprehensive TypeScript types for various control types used in the Clutch editor:
import type { Controls } from '@clutch-creator/toolkit';
// Available control types:
// Array, Checkbox, Code, Color, Combobox, Component, File, Input,
// Json, Media, Number, Object, RichText, Select, Styles, Svg,
// TextArea, Url, ActionExample:
import type { Controls } from '@clutch-creator/toolkit';
type TSomeComponentProps = {
// props.image will use a media control
image: Controls["Media"],
/**
* You can also annotate a prop to set a control
* @control CustomControl
*/
anotherProp: string
}
export function SomeComponent = (props: TSomeComponentProps) {
// ...
}The toolkit is organized into modular exports for specific use cases:
// Individual component imports
import { Image } from '@clutch-creator/toolkit/components/Image';
import { ClientImage } from '@clutch-creator/toolkit/components/ClientImage';
import { Link } from '@clutch-creator/toolkit/components/Link';
import { Seo } from '@clutch-creator/toolkit/components/Seo';
import { RichText } from '@clutch-creator/toolkit/components/RichText';
import { Svg } from '@clutch-creator/toolkit/components/Svg';
import { ApplyHooks } from '@clutch-creator/toolkit/components/ApplyHooks';
import { NotFoundRedirect } from '@clutch-creator/toolkit/components/NotFoundRedirect';
import { Slot } from '@clutch-creator/toolkit/components/Slot';Image: Advanced Next.js Image wrapper with automatic placeholder generation and optimizationClientImage: Client-side image component for dynamic image loadingLink: Enhanced Next.js Link component with support for complex URL parameter managementSeo: Comprehensive SEO component supporting Open Graph, Twitter Cards, and structured dataRichText: Flexible rich text renderer supporting both string and JSX contentSvg: SVG component wrapper for dynamic SVG renderingApplyHooks: Component for applying multiple hooks to children componentsNotFoundRedirect: Component for handling 404 redirectsSlot: Utility component for passing props to children (usescloneChildreninternally)
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Run tests:
bun run test - Run linting:
bun run lint - Create a changeset:
bun run changeset(if your changes should trigger a release) - Commit your changes:
git commit -m 'feat: add amazing feature' - Push to your branch:
git push origin feature/amazing-feature - Create a Pull Request
To run toolkit locally in a clutch project, create a symlink of toolkit:
bun linkThen on the project dir:
bun link @clutch-creator/toolkitYou'll also need to add some configuration to the project next config, make sure the root path is back enough to where it englobes the toolkit location:
const nextConfig: NextConfig = withClutch({
transpilePackages: ['@clutch-creator/toolkit'],
turbo: {
root: path.join(__dirname, '../../../../..'),
},
outputFileTracingRoot: path.join(__dirname, '../../../../..'),
});This project uses Changesets for automated versioning and publishing:
- Create a changeset:
bun run changeset - Push changes to
mainbranch - GitHub Actions will automatically:
- Create a "Release PR" with version updates
- When merged, publish NPM packages and create GitHub releases
MIT
This toolkit is maintained by the Clutch team. For issues and feature requests, please visit our GitHub repository.
- π Documentation
- π Report Issues
- π¬ Community Discussions
- π Official Website
Made with β€οΈ by the Clutch team
Clutch is the next-generation visual builder that empowers creative professionals with total design freedom, advanced functionality, and top-tier performance. Learn more at clutch.io.