A lightweight JavaScript library that automatically formats Urdu poetry into beautifully justified layouts. Supports classical poetic forms including Ghazal, Qat'a, Mukhammas, and Musaddas with intelligent structural awareness.
NPM: https://www.npmjs.com/package/shakeeb-justify
Demo: https://shakesvision.github.io/PoetryJustification/
Urdu poetry is not prose. It has structure:
- Lines must be justified (both edges aligned)
- Couplets (sher) must stay together visually
- Stanzas need proper spacing
- Nastaliq script requires RTL + bidi safety
This library formats poetry the way it is read, not just displayed.
- Zero configuration — just include the script and add classes
- Namespaced CSS — all styles scoped to
.shakeeb-justifyto avoid conflicts - RTL & Nastaliq safe — proper
unicode-bidiand direction handling - Intelligent stanza grouping — automatic gaps between couplets/stanzas
- Classical form support — Ghazal, Qat'a, Mukhammas, Musaddas variants
- Custom patterns —
data-sj-patternanddata-sj-mixedattributes for flexibility - Copy-friendly — copied text preserves line breaks for pasting
- Optional copy buttons — opt-in clipboard buttons for poems/lines
- WordPress/Gutenberg compatible
- No dependencies
Via npm (recommended):
<script src="https://cdn.jsdelivr.net/npm/shakeeb-justify@latest/dist/shakeeb-justify.min.js"></script>Via GitHub:
<script src="https://cdn.jsdelivr.net/gh/shakesvision/PoetryJustification@latest/dist/shakeeb-justify.min.js"></script>npm install shakeeb-justifyimport { ShakeebJustify } from 'shakeeb-justify';
ShakeebJustify.apply();// CommonJS
const { ShakeebJustify } = require('shakeeb-justify');
ShakeebJustify.apply();SSR-safe (since 2.1.0): importing/requiring the package no longer throws in
server-rendered environments (Next.js, Nuxt, plain Node scripts, etc.) where window/
document don't exist at module-load time. Just make sure you call .apply() itself
only on the client (e.g. inside a useEffect/mounted hook), since it needs a real DOM.
<script src="shakeeb-justify.min.js"></script>Standard ghazal layout — one line per row with gap after every couplet (2 lines).
<div class="sj sher">
ہم پہ ساقی کی عنایت سے جلے جاتے ہیں
یہ جو اَب تک کفِ افسوس ملے جاتے ہیں
یاد آ جاتی ہیں بے ساختہ باتیں ان کی
بے سبب ہونٹ تبسم میں ڈھلے جاتے ہیں
</div>Side-by-side layout — pairs of lines displayed in two columns.
<div class="sj sher2">
اِک دن رسولِ پاک نے اصحاب سے کہا
دیں مال راہِ حق میں جو ہوں تم میں مالدار
ارشاد سن کے فرطِ طرب سے عمر اٹھے
اس روز انکے پاس تھے درہم کئی ہزار
</div><div class="sj mukhammas">
خوگر قربت و دیدار پہ کیسی گزرے
کیا خبر اس کے دل زار پہ کیسی گزرے
ہجر میں اس ترے بیمار پہ کیسی گزرے
دور کیا جانیے بد کار پہ کیسی گزرے
تیرے ہی در پہ مرے بیکس و تنہا تیرا
</div><div class="sj mukhammas-3-2">
<!-- First 3 lines, gap, then 2 lines -->
</div><div class="sj mukhammas-mixed">
<!-- Emphasis on final line -->
</div><div class="sj musaddas">
اس قدر شوخ کہ اللہ سے بھی برہم ہے
تھا جو مسجود ملائک یہ وہی آدم ہے
عالم کیف ہے دانائے رموز کم ہے
ہاں مگر عجز کے اسرار سے نامحرم ہے
ناز ہے طاقت گفتار پہ انسانوں کو
بات کرنے کا سلیقہ نہیں نادانوں کو
</div><div class="sj musaddas-6">
<!-- Continuous flow, single unit -->
</div><div class="sj musaddas-mixed">
<!-- Marsiya-style emphasis on closing couplet -->
</div>Define custom grouping patterns using + separator.
<!-- Gap after every 4 lines (Qat'a / Rubai) -->
<div class="sj sher" data-sj-pattern="4">...</div>
<!-- Gap after 3, then 2, repeating -->
<div class="sj sher" data-sj-pattern="3+2">...</div>
<!-- Gap after 6 lines -->
<div class="sj sher" data-sj-pattern="6">...</div>Combine single-column and two-column layouts.
<!-- 4 lines in 2-column, then 2 lines in 1-column -->
<div data-sj-mixed="4:2col,2:1col">...</div>
<!-- 3 lines single, 2 lines double -->
<div data-sj-mixed="3:1col,2:2col">...</div>Modes:
1col— single-column layout2col— two-column layout
Enable copy-to-clipboard functionality with the data-sj-copy attribute. Buttons appear on hover (or tap on mobile).
<!-- Copy all button (top-left of poem) -->
<div class="sj sher" data-sj-copy="all">...</div>
<!-- Per-line copy buttons (appear on row hover) -->
<div class="sj sher" data-sj-copy="row">...</div>
<!-- Both buttons -->
<div class="sj sher" data-sj-copy="both">...</div>Note: Even without copy buttons, poetry copied from this library preserves proper line breaks.
The library normalizes HTML before processing. You can use:
<br>tags<p>paragraphs<div>blocks- Plain text with newlines
All will be converted to clean lines for formatting.
Since 2.1.0, inline formatting inside a poetry line is preserved instead of being
stripped down to plain text — so a footnote marker, a link, or a styled span survives
rendering along with its class/style:
<div class="sj sher">
<p>Line one <sup class="footnote">[1]</sup> continues here</p>
<p>Line two with a <a href="#note-1">reference</a></p>
</div>Supported inline tags: sup, sub, b, strong, i, em, u, mark, small,
span, a, s, del, ins, abbr, code, kbd, q, cite. Allowed attributes:
class, style, id, title, lang, dir, and (for a) href/target/rel.
For safety (poetry markup can come from CMS fields, comments, or user submissions):
<script>,<style>,<iframe>,<object>,<embed>, and similar tags are dropped entirely.- Any other unrecognized tag is unwrapped (its text is kept, the tag itself is removed).
- Event handler attributes (
onclick,onerror, ...) are always stripped. javascript:/data:/vbscript:links are stripped fromhref.
Copying a line (via the copy buttons) still yields clean plain text — tags are removed, only the visible characters are copied.
The library auto-runs on DOMContentLoaded. For dynamic content:
ShakeebJustify.apply();git clone https://github.com/ShakesVision/PoetryJustification.git
cd PoetryJustification
npm installBuild minified version:
npm run buildRun unit tests before publishing:
npm testAll tests must pass before pushing changes to the repository.
- Inline markup preserved inside lines —
<sup>,<b>,<a>,<span class="...">, etc. inside a poetry line are no longer stripped down to plain text (previously the whole line was flattened viatextContent, losing footnote markers, links, and custom styling) - Safe by default — dangerous tags (
<script>,<style>,<iframe>, ...) are dropped, event handler attributes are stripped, andjavascript:/data:links are neutralized - SSR-safe —
require()/importno longer throws whenwindow/documentare unavailable (Next.js, Nuxt, plain Node); propermodule.exportsadded so the documentedimport { ShakeebJustify } from 'shakeeb-justify'actually works - Fixed a hang —
data-sj-mixedpatterns that resolve to zero total lines (e.g. malformed values) now warn and fall back to a single column instead of freezing the tab in an infinite loop - Clipboard hardening — copy buttons no longer throw on non-secure origins/older browsers where
navigator.clipboardis unavailable; a console warning is shown instead - Accessibility — copy buttons now have
aria-label - License changed to Apache-2.0 (see below)
- Scoped selectors — layouts now require
.shakeeb-justifyor.sjwith the method class, e.g..sj.sher - Div/grid output — replaces table markup with SEO-friendlier div-based layout
- Auto layout — adds responsive
automode via.sj.autoordata-sj-method="auto" - Configurable spacing — supports
data-sj-gap,data-sj-sher2-gap, anddata-sj-auto-gap
- Fixed spacer in mixed layouts — no spacer rows within 2-col blocks, gap only between blocks
- Fixed mixed pattern repeating —
.musaddas-mixed,.mukhammas-mixed, anddata-sj-mixednow correctly repeat for long poems - RTL direction on rows — improved RTL support
- Unit tests added — Jest test suite for regression prevention
- RTL table direction — tables now have
direction: rtlfor proper layout - Input validation — console warnings for invalid modes and line count mismatches
- Better error messages — helps debug
data-sj-mixedconfiguration issues
- Copy-friendly output — hidden newlines ensure copied text has proper line breaks
- Copy buttons — opt-in
data-sj-copyattribute for clipboard functionality - RSS/plain-text friendly — line breaks preserved when HTML is stripped
- Namespaced CSS — all styles under
.shakeeb-justifyto prevent conflicts - Spacer rows — gaps implemented via empty rows, not padding (better hover behavior)
- Single table per poem — pattern-based forms now use one table with spacer rows
- Improved code structure — cleaner, documented functions
- Added
data-sj-patternanddata-sj-mixedattributes - Mukhammas variants (standard, 3-2, mixed)
- Musaddas variants (classic 4+2, all-6, mixed)
- Initial release
- Sher & Sher2 formatting
Apache-2.0 © 2020-2026 Shakeeb Ahmad — https://shakeeb.in — free to use, modify
and redistribute, with attribution (keep the LICENSE/NOTICE and the copyright
banner in the bundles). See LICENSE and NOTICE.
Versions published before 2.1.0 remain available under the original MIT license they were released with; this change applies going forward from 2.1.0.
Older instructions, kept for nostalgia.
- Add the script and styles in the pages you want. Refer index.html file in the doc folder for the code.
- Add a new
<div class="sj sher"></div>for one column and<div class="sj sher2"></div>for two columns.
- Write poetry lines inside div tags.
Double column:
- Distributed by space you can change
- Responsive, as widths are defined using percentages
- Both column widths are same irrespective of the content length in them
Single column:
- Gap after every she'r (2 lines)
https://shakesvision.github.io/PoetryJustification/
Note (22 Oct 2025): The older / original version of the demo file can be seen at this commit (since I removed it after converting the project into an NPM package.) See this commit of Mar 12, 2021
Star the repo if you've used it.
Want to see more interesting stuff? Here's my Urdu blog: https://ur.shakeeb.in/
