Watermark
Add personalized watermarks to images using a simple React.

Image Without Watermark
Image after adding watermark

Image with Watermark
Installation
Install dependencies
1npm install pixel-craft --saveCopy the source code
components/ImageCompressor.tsx
1import React, { useEffect, useRef } from 'react';
2
3type Position =
4 | 'top-left'
5 | 'top-right'
6 | 'bottom-left'
7 | 'bottom-right'
8 | 'center'
9 | 'custom';
10
11interface WatermarkedImageProps {
12 baseImageUrl: string;
13 watermarkText?: string;
14 watermarkImageUrl?: string;
15 position?: Position;
16 top?: number;
17 left?: number;
18 bottom?: number;
19 right?: number;
20 opacity?: number;
21 font?: string;
22 textColor?: string;
23 watermarkWidth?: number;
24 watermarkHeight?: number;
25}
26
27const WatermarkedImage: React.FC<WatermarkedImageProps> = ({
28 baseImageUrl,
29 watermarkText,
30 watermarkImageUrl,
31 position = 'bottom-right',
32 top,
33 left,
34 bottom,
35 right,
36 opacity = 0.5,
37 font = '24px Arial',
38 textColor = 'rgba(255, 255, 255, 0.7)',
39 watermarkWidth = 100,
40 watermarkHeight = 50,
41}) => {
42 const canvasRef = useRef<HTMLCanvasElement>(null);
43
44 useEffect(() => {
45 const baseImage = new Image();
46 baseImage.crossOrigin = 'anonymous';
47 baseImage.src = baseImageUrl;
48
49 baseImage.onload = () => {
50 const canvas = canvasRef.current;
51 if (!canvas) return;
52 const ctx = canvas.getContext('2d');
53 if (!ctx) return;
54
55 canvas.width = baseImage.width;
56 canvas.height = baseImage.height;
57
58 ctx.drawImage(baseImage, 0, 0);
59
60 const applyWatermark = () => {
61 ctx.globalAlpha = opacity;
62
63 let x = 0, y = 0;
64
65 const calculatePosition = () => {
66 switch (position) {
67 case 'top-left':
68 x = 10;
69 y = 10;
70 break;
71 case 'top-right':
72 x = baseImage.width - (watermarkWidth + 10);
73 y = 10;
74 break;
75 case 'bottom-left':
76 x = 10;
77 y = baseImage.height - (watermarkHeight + 10);
78 break;
79 case 'bottom-right':
80 x = baseImage.width - (watermarkWidth + 10);
81 y = baseImage.height - (watermarkHeight + 10);
82 break;
83 case 'center':
84 x = (baseImage.width - watermarkWidth) / 2;
85 y = (baseImage.height - watermarkHeight) / 2;
86 break;
87 case 'custom':
88 x = left ?? baseImage.width - (right ?? watermarkWidth + 10) - watermarkWidth;
89 y = top ?? baseImage.height - (bottom ?? watermarkHeight + 10) - watermarkHeight;
90 break;
91 default:
92 x = 10;
93 y = 10;
94 }
95 };
96
97 calculatePosition();
98
99 if (watermarkText) {
100 ctx.font = font;
101 ctx.fillStyle = textColor;
102 ctx.fillText(watermarkText, x, y + watermarkHeight);
103 } else if (watermarkImageUrl) {
104 const watermarkImage = new Image();
105 watermarkImage.crossOrigin = 'anonymous';
106 watermarkImage.src = watermarkImageUrl;
107 watermarkImage.onload = () => {
108 ctx.drawImage(watermarkImage, x, y, watermarkWidth, watermarkHeight);
109 };
110 }
111
112 ctx.globalAlpha = 1.0;
113 };
114
115 applyWatermark();
116 };
117 }, [
118 baseImageUrl,
119 watermarkText,
120 watermarkImageUrl,
121 position,
122 top,
123 left,
124 bottom,
125 right,
126 opacity,
127 font,
128 textColor,
129 watermarkWidth,
130 watermarkHeight,
131 ]);
132
133 return <canvas ref={canvasRef} />;
134};
135
136export default WatermarkedImage;Props
| Prop name | Type | Required | Description |
|---|---|---|---|
baseImageURL | String | (required) | URL of the base image to apply watermark on. |
watermarkText | String | undefined | Text content of the watermark (either this orwatermarkImageUrlis used). |
watermarkImageUrl | string | undefined | URL of an image to be used as the watermark. |
position | 'top-left' | 'bottom-right' | Predefined or custom positioning of the watermark. |
top | number | undefined | Top offset for custom positioning. |
left | number | undefined | Left offset for custom positioning. |
bottom | number | undefined | Bottom offset for custom positioning. |
right | number | undefined | Right offset for custom positioning. |
opacity | number | 0.5 | Opacity of the watermark (0 to 1). |
font | string | '24px Arial' | Font style used when applying text watermark. |
textColor | string | 'rgba(255,255,255,0.7)' | Color of the watermark text. |
watermarkWidth | number | 100 | Width of watermark image (if using watermarkImageUrl ). |
watermarkHeight | number | 50 | Height of watermark image. |
Possible Errors and User Measures
| Error/ Issue | Cause | User's Measures/ Fixes |
|---|---|---|
| Base image fails to load | Incorrect or no CORS headersbaseImageUrl | Ensure the URL is correct and the server supports CORS(Access-Control-Allow-Origin). |
| Watermark image not shown | WrongwatermarkImageUrlor CORS restriction | Same as above: use accessible images and check CORS headers. |
| Watermark text is not visible | Poor textColor contrast with base image | Use high-contrast colors or semi-transparent backgrounds for text. |
| Canvas size is 0 | Image fails to load before drawing | Add error handling (onError) and conditionally render fallback UI. |
| Watermark image not positioned properly | nvalid custom position values (top, left, right, bottom) | Validate and clamp position values before use. |
| Image not updated when props change | Renders once and doesn’t reflect updated props | Ensure useeffect dependency list includes all props affecting rendering (which you've already done well). |
Real World Uses
Branding product images
Automatically add company logo to product or catalog images before display/upload.
Watermarking user-uploaded content
Add a copyright notice or username to user-uploaded photos in CMS, marketplaces, or design tools.
Protecting images in gallery/portfolio
Prevent misuse by watermarking preview images with your brand or signature.
Batch watermarking
Use in a loop to process multiple images and save/download them.
Optimization Techniques
| Techniques | Benefits |
|---|---|
Use requestAnimationFrame or async chunking for canvas work | Prevents UI freezing on large image loads or batch processing. |
| Convert canvas to Blob instead of DataURL for large outputs | More memory-efficient and suitable for upload. |
| Preload watermark image | Avoid delayed drawing on first render; useImage with before canvas use. or onload to prevent UI freezing. |
| Allow setting watermark scaling relative to base image size | Makes watermark responsive to different image resolutions. |
| Debounce rerenders if props update rapidly | Useful if image URL or options are updated frequently (e.g., sliders/UI tools). |