# How to Add a New PDF Processing Tool Using the useToolOperation Hook Pattern in Stirling-PDF

> Learn to add a new PDF processing tool to Stirling-PDF using the useToolOperation hook pattern. Follow our guide to extend parameters, build forms, and register your operation.

- Repository: [Stirling Tools/Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF)
- Tags: how-to-guide
- Published: 2026-03-01

---

**To add a new PDF processing tool in Stirling-PDF, create a parameter hook that extends `useBaseParameters`, define a pure `FormData` builder function, export a static `operationConfig` object, and wrap everything in a hook that calls the shared `useToolOperation` hook, then register the tool in [`toolsTaxonomy.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/toolsTaxonomy.ts) with its `operationConfig`.**

Stirling-PDF implements every PDF-processing feature as a small React hook that wraps the shared `useToolOperation` hook pattern. This architecture provides consistent error handling, progress tracking, and credit checks while maintaining a single source of truth for API endpoints and form-data construction. Whether you are adding a Watermark tool or a custom compression feature, following this pattern ensures your new PDF processing tool integrates seamlessly with the UI and automation system.

## 1. Define the Parameter Shape and Hook

Create a [`useWatermarkParameters.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useWatermarkParameters.ts) file following the pattern established by existing tools like Split. Define the interface extending `BaseParameters` and use `useBaseParameters` to manage state.

```typescript
// src/core/hooks/tools/watermark/useWatermarkParameters.ts
import { BaseParameters } from '@app/types/parameters';
import { useBaseParameters, BaseParametersHook } from '@app/hooks/tools/shared/useBaseParameters';
import { ENDPOINTS, WATERMARK_METHODS } from '@app/constants/watermarkConstants';

export interface WatermarkParameters extends BaseParameters {
  text: string;
  opacity: string;               // 0‑1 as string
  rotation: string;              // degrees
  // add any extra fields you need
}

export type WatermarkParametersHook = BaseParametersHook<WatermarkParameters>;

export const defaultParameters: WatermarkParameters = {
  text: '',
  opacity: '0.5',
  rotation: '0',
};

export const useWatermarkParameters = (): WatermarkParametersHook => {
  return useBaseParameters({
    defaultParameters,
    endpointName: () => ENDPOINTS[WATERMARK_METHODS.ADD],
    validateFn: (p) => p.text.trim().length > 0 && p.opacity !== '' && p.rotation !== '',
  });
};

```

This pattern mirrors the **Split** parameters hook implementation in [[`useSplitParameters.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useSplitParameters.ts)](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/frontend/src/core/hooks/tools/split/useSplitParameters.ts).

## 2. Build the FormData Builder

Most tools send a `multipart/form-data` request. Create a pure function that receives the parameters and a `File`, returning a `FormData` instance.

```typescript
// src/core/hooks/tools/watermark/useWatermarkOperation.ts (part)
export const buildWatermarkFormData = (params: WatermarkParameters, file: File): FormData => {
  const form = new FormData();
  form.append('fileInput', file);
  form.append('watermarkText', params.text);
  form.append('opacity', params.opacity);
  form.append('rotation', params.rotation);
  return form;
};

```

Compare this with the Rotate builder in [[`useRotateOperation.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useRotateOperation.ts)](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/frontend/src/core/hooks/tools/rotate/useRotateOperation.ts).

## 3. Create the Static Operation Configuration

Export a constant that describes the tool for **useToolOperation**. This object is also used by the automation engine.

```typescript
// src/core/hooks/tools/watermark/useWatermarkOperation.ts (continued)
export const watermarkOperationConfig = {
  toolType: ToolType.singleFile,            // most PDF tools are single‑file
  buildFormData: buildWatermarkFormData,
  operationType: 'watermark' as const,     // matches a ToolId entry
  endpoint: '/api/v1/misc/watermark-pdf', // backend endpoint
  defaultParameters,
} as const;

```

See the Rotate static config in [[`useRotateOperation.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useRotateOperation.ts)](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/frontend/src/core/hooks/tools/rotate/useRotateOperation.ts).

## 4. Export the Hook that Calls useToolOperation

```typescript
// src/core/hooks/tools/watermark/useWatermarkOperation.ts (final)
import { useTranslation } from 'react-i18next';
import { createStandardErrorHandler } from '@app/utils/toolErrorHandler';
import { ToolType, useToolOperation } from '@app/hooks/tools/shared/useToolOperation';

export const useWatermarkOperation = () => {
  const { t } = useTranslation();

  return useToolOperation<WatermarkParameters>({
    ...watermarkOperationConfig,
    getErrorMessage: createStandardErrorHandler(
      t('watermark.error.failed', 'An error occurred while adding the watermark.')
    ),
  });
};

```

This pattern is identical to [`useCompressOperation.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useCompressOperation.ts) and [`useRotateOperation.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useRotateOperation.ts).

## 5. Register the Tool in the Global Registry

Add an entry to [`src/core/data/toolsTaxonomy.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/core/data/toolsTaxonomy.ts). Provide the component, icon, and `operationConfig`.

```typescript
// src/core/data/toolsTaxonomy.ts (excerpt)
import WatermarkTool from '@app/components/tools/WatermarkTool';
import WatermarkIcon from '@mui/icons-material/Watermark';   // any MUI icon

export const toolRegistry: ToolRegistry = {
  // … existing tools
  watermark: {
    icon: <WatermarkIcon />,
    name: 'Watermark',
    component: WatermarkTool,
    description: 'Add a custom text watermark to PDFs.',
    categoryId: ToolCategoryId.STANDARD_TOOLS,
    subcategoryId: SubcategoryId.PAGE_FORMATTING,
    maxFiles: 1,
    supportedFormats: ['pdf'],
    operationConfig: watermarkOperationConfig,          // <-- critical!
    automationSettings: null,
  },
};

```

Every existing entry follows this shape; for example the **Rotate** entry uses `rotateOperationConfig` (see [`toolsTaxonomy.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/toolsTaxonomy.ts) around line 40).

## 6. Create the UI Component (Optional but Recommended)

A thin wrapper component connects the parameters hook to the generic **ToolRenderer**.

```tsx
// src/components/tools/WatermarkTool.tsx
import { useWatermarkParameters } from '@app/hooks/tools/watermark/useWatermarkParameters';
import { useWatermarkOperation } from '@app/hooks/tools/watermark/useWatermarkOperation';
import { ToolBase } from '@app/components/tools/ToolBase';

export const WatermarkTool = () => {
  const paramsHook = useWatermarkParameters();
  const operation = useWatermarkOperation();

  return (
    <ToolBase
      title="Watermark"
      parametersHook={paramsHook}
      operation={operation}
    />
  );
};

```

All existing tools (e.g., **Rotate**, **Compress**) use the same `ToolBase` wrapper, guaranteeing a uniform UI and correct interaction with `FileContext`.

## 7. Verify Automation Support

Because `operationConfig` is stored in the registry, the automation engine automatically knows how to invoke the new tool. A quick sanity test:

```typescript
// src/core/utils/automationExecutor.test.ts (conceptual)
await executeAutomationSequence(
  [{ operation: 'watermark', parameters: { text: 'Demo', opacity: '0.3', rotation: '45' } }],
  [sampleFile],
  toolRegistry,
  console.log,
  () => {},    // no‑op callbacks
  (i, err) => { throw err; }
);

```

If the sequence runs without errors, the tool is fully automation‑compatible.

The custom **Automate** tool ([`useAutomateOperation.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useAutomateOperation.ts)) shows how any tool can be called via `executeAutomationSequence`.

## 8. Run the Test Suite

The project already ships with unit tests for each tool hook (e.g., [`useRotateOperation.test.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useRotateOperation.test.ts)). Add a similar test file ([`useWatermarkOperation.test.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useWatermarkOperation.test.ts)) that:

1. Mocks `useToolOperation` to capture the configuration passed.
2. Asserts that `toolType`, `endpoint`, and `buildFormData` are correct.

Running [`./test.sh`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/./test.sh) (or `npm test` inside `frontend`) should now include the new test without failures.

## Summary

- **Create a parameter hook** extending `useBaseParameters` with validation logic in `src/core/hooks/tools/{tool}/use{Tool}Parameters.ts`.
- **Implement a pure FormData builder** function for `multipart/form-data` construction.
- **Define a static operation configuration** object containing `toolType`, `endpoint`, `buildFormData`, and `operationType` for automation support.
- **Export the main operation hook** that wraps `useToolOperation` with error handling via `createStandardErrorHandler`.
- **Register the tool** in [`src/core/data/toolsTaxonomy.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/core/data/toolsTaxonomy.ts) with the `operationConfig` reference to enable UI and automation discovery.
- **Build a thin UI component** using `ToolBase` to ensure consistent file handling and progress display.
- **Verify automation compatibility** through `executeAutomationSequence` and run the full test suite to ensure integration.

## Frequently Asked Questions

### How does the useToolOperation hook handle errors?

The `useToolOperation` hook accepts a `getErrorMessage` function that creates standardized error handlers. Pass a translation key or default message to `createStandardErrorHandler` from `@app/utils/toolErrorHandler`, which returns a function that formats API errors consistently across all PDF processing tools. This ensures users see localized, actionable error messages regardless of which specific tool fails.

### Can I use this pattern for multi-file PDF operations?

Yes. Change the `toolType` in your `operationConfig` to `ToolType.multiFile` instead of `ToolType.singleFile`. The `useToolOperation` hook automatically adjusts progress tracking and FormData construction to handle multiple files, and the automation engine will process file arrays accordingly. Ensure your `buildFormData` function iterates over the files array if the backend expects multiple `fileInput` entries.

### What is the minimum required configuration to register a new tool?

You must provide the `operationConfig` object containing `toolType`, `endpoint`, `buildFormData`, `operationType`, and `defaultParameters` when registering in [`toolsTaxonomy.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/toolsTaxonomy.ts). Without this configuration, the automation engine cannot invoke your tool and the `useToolOperation` hook lacks the necessary metadata to execute API calls. The registry entry must also include `component`, `icon`, and `supportedFormats` for UI rendering.

### How do I test the new tool hook before integrating the UI?

Create a unit test file (e.g., [`useWatermarkOperation.test.ts`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/useWatermarkOperation.test.ts)) that mocks `useToolOperation` to verify the configuration object passed contains the correct `endpoint`, `toolType`, and `buildFormData` function. Run the test suite with `npm test` or [`./test.sh`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/./test.sh) to ensure the hook exports valid metadata before building the UI component. This validates that the hook correctly wraps `useToolOperation` before React components consume it.