Skip to content

Instantly share code, notes, and snippets.

@soulwire
Created October 10, 2025 23:48
Show Gist options
  • Select an option

  • Save soulwire/9ad9bf5b5340a19a58e5807332d5d31f to your computer and use it in GitHub Desktop.

Select an option

Save soulwire/9ad9bf5b5340a19a58e5807332d5d31f to your computer and use it in GitHub Desktop.
SVG Nine Slice Border (React)
interface SvgNineSliceBorderProps {
// Source image URL
imageUrl: string;
// Source image width in pixels
srcWidth: number;
// Source image height in pixels
srcHeight: number;
// Slice markers in source pixels
slices: [number, number, number, number];
// Border thickness in output units
borderWidth: number;
// Width in output units
width: number;
// Height in output units
height: number;
// Whether to fill the center region
fill?: boolean;
// X position in output units
x?: number;
// Y position in output units
y?: number;
}
export function SvgNineSliceBorder({
imageUrl,
srcWidth,
srcHeight,
slices,
borderWidth,
width,
height,
fill = false,
x = 0,
y = 0,
}: SvgNineSliceBorderProps) {
const [t, r, b, l] = slices;
// Guard against pathological slices
const midSrcW = Math.max(0, srcWidth - l - r);
const midSrcH = Math.max(0, srcHeight - t - b);
// Clamp border thickness to avoid overlaps
const bwX = Math.min(borderWidth, Math.floor(width / 2));
const bwY = Math.min(borderWidth, Math.floor(height / 2));
const innerW = Math.max(0, width - 2 * bwX);
const innerH = Math.max(0, height - 2 * bwY);
return (
<g x={x} y={y}>
{/* Corners */}
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={0}
sy={0}
sw={l}
sh={t}
x={0}
y={0}
width={bwX}
height={bwY}
/>
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={srcWidth - r}
sy={0}
sw={r}
sh={t}
x={width - bwX}
y={0}
width={bwX}
height={bwY}
/>
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={0}
sy={srcHeight - b}
sw={l}
sh={b}
x={0}
y={height - bwY}
width={bwX}
height={bwY}
/>
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={srcWidth - r}
sy={srcHeight - b}
sw={r}
sh={b}
x={width - bwX}
y={height - bwY}
width={bwX}
height={bwY}
/>
{/* Edges */}
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={l}
sy={0}
sw={midSrcW}
sh={t}
x={bwX}
y={0}
width={innerW}
height={bwY}
/>
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={l}
sy={srcHeight - b}
sw={midSrcW}
sh={b}
x={bwX}
y={height - bwY}
width={innerW}
height={bwY}
/>
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={0}
sy={t}
sw={l}
sh={midSrcH}
x={0}
y={bwY}
width={bwX}
height={innerH}
/>
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={srcWidth - r}
sy={t}
sw={r}
sh={midSrcH}
x={width - bwX}
y={bwY}
width={bwX}
height={innerH}
/>
{fill && (
<SvgNineSliceBorderTile
imageUrl={imageUrl}
srcWidth={srcWidth}
srcHeight={srcHeight}
sx={l}
sy={t}
sw={midSrcW}
sh={midSrcH}
x={bwX}
y={bwY}
width={innerW}
height={innerH}
/>
)}
</g>
);
}
interface SvgNineSliceBorderTileProps {
// Source image URL
imageUrl: string;
// Source image width in pixels
srcWidth: number;
// Source image height in pixels
srcHeight: number;
// X position of the tile within the source image in pixels
sx: number;
// Y position of the tile within the source image in pixels
sy: number;
// Width of the tile within the source image in pixels
sw: number;
// Height of the tile within the source image in pixels
sh: number;
// X position of the tile in output units
x: number;
// Y position of the tile in output units
y: number;
// Width of the tile in output units
width: number;
// Height of the tile in output units
height: number;
}
function SvgNineSliceBorderTile({
imageUrl,
srcWidth,
srcHeight,
sx,
sy,
sw,
sh,
x,
y,
width,
height,
}: SvgNineSliceBorderTileProps) {
return (
<svg
x={x}
y={y}
width={width}
height={height}
viewBox={`${sx} ${sy} ${sw} ${sh}`}
preserveAspectRatio="none"
>
<image href={imageUrl} width={srcWidth} height={srcHeight} preserveAspectRatio="none" />
</svg>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment