Skip to content

Instantly share code, notes, and snippets.

Last active Jul 30, 2021
What would you like to do?
Tailwind UI -> Valid React

Community based - Examples to React

DISCLAIMER: This is something a Tailwind UI user created. This also doesn't actually add all the functionality, however it will ensure that the markup is valid JSX. Search for todo and you will see all the alpine stuff that you can convert yourself.

WARNING: This doesn't work in Firefox because they don't allow to read from the clipboard. They only allow it in extensions.

How to use

  1. Create a bookmark in your browser where the url is set to the contents of bookmark.js 1.1. Copy bookmark.js

    1.2. Create a new bookmark in your browser. I am using Brave here.

    1.3. Paste in the contents

  2. Go to Tailwind UI

  3. Press the "Copy to clipboard" button on an example

  4. Press the bookmark

  5. You should receive an alert saying that it is either copied OR has an error.

  6. Profit.

javascript:window.navigator.clipboard.readText().then((data)=>{return window.navigator.clipboard.writeText(data .replace(/class=/g,"className=").replace(/ @([^"]*)=/g,(_all,group)=>` data-todo-at-${group.replace(/[.:]/g,"-")}=`).replace(/ x-([^ "]*)/g,(_all,group)=>` data-todo-x-${group.replace(/[.:]/g,"-")}`).replace(/<!--/g,"{/*").replace(/-->/g,"*/}").replace(/tabindex="([^"]*)"/g,"tabIndex={$1}").replace(/datetime=/g,"dateTime=").replace(/clip-rule=/g,"clipRule=").replace(/fill-rule=/g,"fillRule=").replace(/stroke-linecap=/g,"strokeLinecap=").replace(/stroke-width=/g,"strokeWidth=").replace(/stroke-linejoin=/g,"strokeLinejoin=").replace(/for=/g,"htmlFor=").replace(/ :(.*)=/g," data-todo-colon-$1=").replace(/href="#"/g,'href="/"').replace(/src="\//g,'src="').replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,"").replace(/<(img|input|hr)([^>]*)>/g,"<$1$2 />").replace(/style="([^"]*)"/g,(_,style)=>{return `style={{${style.split(";").filter((pair)=>pair.trim().length).map((pair)=>pair.split(":").map((part)=>part.trim())).map(([key,value])=>[key.replace(/-(\w)/g,(_,v)=>v.toUpperCase()),JSON.stringify(value.match(/\d*px/)?parseFloat(value):value)].join(": ")).join(", ")}}}`}).trim()).then(()=>{alert("Copied to clipboard!")})}).catch((err)=>{console.log("[TAILWIND UI - Example -> React] Error:",err);alert("Bummer! Something went wrong... (psst, see devtools)")});
// Note this doesn't work as a bookmark, you need the bookmark (minified) version
javascript: window.navigator.clipboard
.then((data) => {
return window.navigator.clipboard
// Replace `class=` with `className=`
.replace(/class=/g, "className=")
// Replace all attributes starting with @.
// E.g.: `@click.stop` -> `data-todo-at-stop`
/ @([^"]*)=/g,
(_all, group) => ` data-todo-at-${group.replace(/[.:]/g, "-")}=`
// Replaces all attributes starting with x-.
// E.g.: `x-transition:enter` -> `data-todo-x-transition-enter`
/ x-([^ "]*)/g,
(_all, group) => ` data-todo-x-${group.replace(/[.:]/g, "-")}`
// Replace html comments with JSX comments
.replace(/<!--/g, "{/*")
.replace(/-->/g, "*/}")
// Replace `tabindex="0"` with `tabIndex={0}`
.replace(/tabindex="([^"]*)"/g, "tabIndex={$1}")
// Replace `datetime` with `dateTime` for <time />
.replace(/datetime=/g, "dateTime=")
// Replace `clip-rule` with `clipRule` in svg's
.replace(/clip-rule=/g, "clipRule=")
// Replace `fill-rule` with `fillRule` in svg's
.replace(/fill-rule=/g, "fillRule=")
// Replace `stroke-linecap` with `strokeLinecap` in svg's
.replace(/stroke-linecap=/g, "strokeLinecap=")
// Replace `stroke-width` with `strokeWidth` in svg's
.replace(/stroke-width=/g, "strokeWidth=")
// Replace `stroke-linejoin` with `strokeLinejoin` in svg's
.replace(/stroke-linejoin=/g, "strokeLinejoin=")
// Replace `for` with `htmlFor` in forms
.replace(/for=/g, "htmlFor=")
// Replace all attributes starting with :.
// E.g.`:class="{ 'hidden': open, 'inline-flex': !open` ->
// `data-todo-colon-class="{ 'hidden': open, 'inline-flex': !open }"`
.replace(/ :(.*)=/g, " data-todo-colon-$1=")
// Replace `href="#"` with `href="/"` (Otherwise Create React App complains)
.replace(/href="#"/g, 'href="/"')
// Replace relative src paths with absolute src paths.
.replace(/src="\//g, 'src="')
// Drop scripts ¯\_(ツ)_/¯
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "")
// Ensure img, input and hr tags are self closed
.replace(/<(img|input|hr)([^>]*)>/g, "<$1$2 />")
// Ensure we rewrite the style string to an object
.replace(/style="([^"]*)"/g, (_, style) => {
return `style={{${style
.filter((pair) => pair.trim().length)
.map((pair) => pair.split(":").map((part) => part.trim()))
.map(([key, value]) =>
key.replace(/-(\w)/g, (_, v) => v.toUpperCase()),
value.match(/\d*px/) ? parseFloat(value) : value
].join(": ")
.join(", ")}}}`;
// Trim the whitespace!
.then(() => {
alert("Copied to clipboard!");
.catch((err) => {
console.log("[TAILWIND UI - Example -> React] Error:", err);
alert("Bummer! Something went wrong... (psst, see devtools)");
Copy link

markitics commented Nov 20, 2020

This is great, thanks. I normally hide the bookmarks bar, so today I learned on Mac I can press Cmd-Shift-B (Ctrl-Shift-B on Windows) to toggle show/hide bookmark bar (in Chrome).

Copy link

stevecastaneda commented Dec 19, 2020

Couple to add that I noticed today:

// Replace `autocomplete` with `autoComplete` in inputs
.replace(/autocomplete=/g, "autoComplete=")


// Replace `rows="0"` with `rows={0}` in textareas
.replace(/rows="([^"]*)"/g, "rows={$1}")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment