/html

Primary LanguageJavaScript

Screnshot of `html` with syntax highlighting in VSCode

html: JSX-Like Syntax for Tagged Template Literals

A couple blog posts detailing the rationale for an using tagged template literals:

Usage

Copy/paste the contents of html.js into your own codebase.

Or, install it in your project (this isn't published on NPM, but you can install directly from GitHub or pull it in from a service like jsdeliver):

  • Node: npm i jimniels/html
  • Browser: import html from "https://cdn.jsdelivr.net/gh/jimniels/html@0.2.0/html.js"

Why?

Sometimes you just don’t need a bespoke templating languages with its quirks and dependneices. Just Use JavaScript™️.

Example trade-offs of using template literals over JSX:

  • No more trying to remember which custom attributes require their own spcial syntax. Just use the platform standard names. For example:
    • class="" instead of className=""
    • for="" instead of htmlFor=""
    • style="font-weight: 600" isntead of style={{ fontWeight: 600 }}
    • Use standard SVG attributes too without worry! e.g. xlink:href instead of xlinkHref
  • Render more than just HTML without a problem: SVG, JSON, whatever! It’s all just text. No need to try and do special fragment workarounds.
  • No dangerouslySetInnerHTML (in a sense, everything in template literals is dangerouslySetInnerHTML, you just escape the things you know are unsafe).
  • Do templating in the script tags in your HTML, e.g. <script>var t = "${myVar}";</script>

Examples

Everything in template literals is an expression. The tagged html template literal will handle coercing JSX-like expressions into the correct strings for you.

Render a string or number:

const props = { title: "Hello World" };

const Component = (props) => html`
  <h1>${props.title}</h1>
`;

Component(props);
// -> "<h1>Hello World</h1>"

Note: line breaks and indents will appear in multi-line strings. There are libraries to help with this if you need them.

“Components” are merely functions that take arguments and return strings:

const H1 = ({ text }) => html`
  <h1>${text}</h1>
`;

const H2 = ({ text }) => html`
  <h2>${text}</h2>
`;

const out = `<!doctype html>
  <html>
    <body>
      ${H1({ text: "Hello" })}
      ${H2({ text: "World" })}
    </body>
  </html>
`;

// -> "<!doctype html><html><body><h1>Hello</h1><h2>World</h2></body></html>"

Use conditionals and arrays just like in JSX too!

const props = {
  activePath: "/about/",
  navItems: [
    { path: "/", title: "Home" },
    { path: "/about/", title: "About" },
    { path: "/tags/", title: "Tags" }
  ]
};

const Navigation = ({ activePath, navItems }) => html`
  <ul class="navigation">
    ${navItems.map(({ path, title }) => html`
      <li>
        <a
          class="${activePath === path && 'active'}"
          href="${path}">
          ${title}
        </a>
      </li>
    `)}
  </ul>
`;

Navigation(props);
// -> <ul class="navigation"><li><a class="" href="/">Home</a></li>...</ul>

And make sure you get syntax highlighting for the HTML embedded in template literals.

Read the original blog post for more details.