Skip to content

Instantly share code, notes, and snippets.

Last active March 29, 2024 08:00
Show Gist options
  • Save RobinMalfait/a90e8651196c273dfa51eec0f43e1676 to your computer and use it in GitHub Desktop.
Save RobinMalfait/a90e8651196c273dfa51eec0f43e1676 to your computer and use it in GitHub Desktop.
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

mokadev commented Feb 28, 2020


thanks for this Gist!

You should add a replacement for stroke-linecap (=> strokeLinecap), stroke-linejoin (=> strokeLinejoin) and stroke-width (=> strokeWidth)

Copy link

I cannot understand step 1 at all. Can you put it visually like a screenshot or something?

Copy link

@waldothedeveloper do you know how to add a bookmark to your browser's bookmarks bar?

Copy link


yes I do this what you mean?


Perhaps I didn't formulate my question correctly.

I'd say that the rest of the steps don't work for me.
That's why I asked you for like a step by step screenshots.

Copy link

@waldothedeveloper but if you figured that out, what exactly is the "the steps don't work for me", what do you have issues with?

I updated the readme with images, if this doesn't help you than I can't help you further.

Copy link

@RobinMalfait thank you for adding the screenshots, I appreciate that.

Copy link

vvo commented Mar 12, 2020

Hey thanks this is extremely useful :)

Copy link

vvo commented Mar 12, 2020

@RobinMalfait and others, I made a small UI to explain how to use this, with an easy drag and drop button

Copy link

Thank you for this, @RobinMalfait!

Copy link

JuanLuisGarciaBorrego commented Apr 20, 2020

Hello, thanks you!
How do you implement the effects in ReactJS?

<div className={`md:hidden ${showNav ? 'block' : 'hidden'} `}></div>

    Off-canvas menu overlay, show/hide based on off-canvas menu state.

   Entering: "transition-opacity ease-linear duration-300"
   From: "opacity-0"
  To: "opacity-100"
  Leaving: "transition-opacity ease-linear duration-300"
   From: "opacity-100"
   To: "opacity-0"

Copy link

@vvo thank you very much!!

Copy link

Just a quick FYI, it might be worth adding a note that this snippet doesn't work in Firefox, at least as far as I can tell.

Copy link

ecklf commented Aug 14, 2020

Made a grease/tampermonkey script with minor modifications: Tailwind UI -> Valid React


Copy link

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

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