Getting Started with LitElement and Tailwind
Tags: javascripttailwindcs
Published: 2021-02-25 17:32:42

---
title: Getting Started with LitElement and Tailwind
published: true
date: 2021-02-23 17:21:29 UTC
tags: javascripttailwindcs
canonical_url: https://www.thisdot.co/blog/getting-started-with-litelement-and-tailwind
---

# Getting Started with LitElement and Tailwind

By taking advantage of LitElement's thin layer over Web Components combined with Tailwind's near infinite offering of CSS utility classes, you can create a beautiful, completely custom, and incredibly performant data-driven user-interface with ease.

## Tooling

We will be using a combination of tools to give us as great of a developer experience as possible, as quickly as possible.

- [RollupJS](https://rollupjs.org/guide/en/) - Rollup's approach to module bundling relies on the native JavaScript module standard, ES Modules, as opposed to Node's CommonJS module system.
- [Babel](https://babeljs.io/) - Babel will allow us to use the latest JavaScript features by compiling down our code to ES5, something older browsers will understand.
- [TypeScript](https://www.typescriptlang.org/) - Better to have silly errors during development than insanity-inducing errors in production. TypeScript's got our backs by reducing the bugs (accidental features?) we introduce in our code.
- [ESLint](https://eslint.org/)
- [Prettier](https://prettier.io/)

### Installing RollupJS and initial configuration

Rollup has an excellent [starter-app](https://github.com/rollup/rollup-starter-app) template that we can use to get going quickly. Clone their repo and we'll use that as our starting point.

Create/Edit/Remove these files:

- `LICENSE` - This is just a prototype, so we don't need this file.
- `README.md` - Again, this is just a prototype, so we don't need this either.
- `src/update.js` - We can remove this.
- `public/index.html` - We can remove the `<h1>` and the `<p>` tags. We'll start fresh.
- `package.json` - Remove the `date-fns` dependency.
- `src/main.js` - Update it so it has a single command to test our config, `console.log('Hello, World!')`

After that's done, let's test our initial Rollup setup:

1. `yarn install` or `npm install` to install our dependencies
2. `yarn dev` or `npm dev` to run our development environment
3. Navigate to [localhost:5000](http://localhost:5000/)
4. Open dev-tools to see our test message

[![Logging Hello, World!](https://images.ctfassets.net/zojzzdop0fzx/s1E993WiZMp6lRXK5FThM/a588e11c767f07dbf368034b531d54fd/hello-world.png)](https://images.ctfassets.net/zojzzdop0fzx/s1E993WiZMp6lRXK5FThM/a588e11c767f07dbf368034b531d54fd/hello-world.png)

### Configuring Babel & TypeScript

To configure Babel and TypeScript, we'll take advantage of Rollup's Babel [plugin](https://www.npmjs.com/package/@rollup/plugin-babel), and we'll configure babel to compile our TypeScript code.

Install the following dependencies:

- `@babel/core`
- `@rollup/plugin-babel`
- `@babel/preset-env`
- `typescript`
- `@rollup/plugin-typescript`
- `@babel/preset-typescript`

Edit the following files:

- `src/main.js` rename to `src/main.ts`
- `rollup.config.js` 

Import and use the Babel plugin, and set the input file as a `.ts` file (for TypeScript).

```
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import typescript from '@rollup/plugin-typescript';
import { babel } from '@rollup/plugin-babel'

// `npm run build` -> `production` is true
// `npm run dev` -> `production` is false
const production = !process.env.ROLLUP_WATCH;

export default {
  input: 'src/main.ts',
  output: {
    file: 'public/bundle.js',
    format: 'es',
    sourcemap: true
  },
  plugins: [
    resolve(),
    commonjs(),
    typescript({
      include: ['src/**/*.ts']
    }),
    babel({
      babelHelpers: 'bundled',
      exclude: "node_modules/**"
    }),
    production && terser({ format: { comments: false } }), // minify, but only in production
 ]
};
```

- `.babelrc` - Create and setup Babel configuration file to compile down to a minimum of ES5 code.

```
{
  "presets": ["@babel/preset-env"]
}
```

- `tsconfig/json` - Create the TypeScript configuration file with the command `yarn tsc --init`

### Configuring ESLint and Prettier

Install the following dependencies:

- `eslint`
- `prettier`
- `eslint-config-prettier`
- `eslint-plugin-prettier`

Run `yarn eslint --init` and choose the following options to create `.eslintrc.js`, ESLint's config file.

- enforce code style
- JavaScript modules
- No Framework
- TypeScript
- Project runs in the browser
- Use Airbnb style guide
- config format JavaScript

After this, we can see that our IDE (in my scenario, VS Code) recognizes the ESLint configuration and is throwing some issues my way.

![ESLint Errors](https://labs.thisdot.co/blog/pictures/eslint-working.png)[![eslint-working](https://images.ctfassets.net/zojzzdop0fzx/exZ4KnARh9wxGXrYNBisC/f268c9b0a335b555dc59387c63262ee0/eslint-working.png)](https://images.ctfassets.net/zojzzdop0fzx/exZ4KnARh9wxGXrYNBisC/f268c9b0a335b555dc59387c63262ee0/eslint-working.png)

In `.eslintrc.js`

- Add `'plugin:prettier/recommended'` to the `extends` array
- Add `'prettier'` to the plugins array
- Add `"prettier/prettier": "error"` to the rules object

Create and configure `.prettierrc`, Prettier's configuration file. This single line is for any Window's developers, preventing any weird end-of-line prettier issues.

```
{
  "endOfLine": "auto"
}
```

[![ESLint](https://images.ctfassets.net/zojzzdop0fzx/2EOJOOUKRcbHEJ0VvZeI32/c5fdcb62f5f526ea6a78c73379875fd6/eslint-and-prettier-working.png)](https://images.ctfassets.net/zojzzdop0fzx/2EOJOOUKRcbHEJ0VvZeI32/c5fdcb62f5f526ea6a78c73379875fd6/eslint-and-prettier-working.png)

## Using LitElement to make a data-driven User Interface

Now that we have a solid tooling setup, lets code up some UI with [LitElement](https://lit-element.polymer-project.org/).

LitElement is a thin wrapper around [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components). It provides us a way to declaratively write our UI as a function of state. A difference though between LitElement and another framework like React or Vue is that LitElement is built on top of the native component model of the web.

Let's code the obligatory 'Hello, World!'

First, install `lit-element` as a dependency to the project:

`yarn add lit-element`

To use a web component, we'll define it in `src/main.ts`, and then we'll use the new component in `public/index.html`.

`src/main.ts`

```
import { LitElement, customElement, html } from "lit-element";

@customElement("app-component")
export default class AppComponent extends LitElement {
  render() {
    return html` <h1>Hello, World!</h1> `;
  }
}
```

`public/index.html`

```
<!doctype html>
<html>

<head lang='en'>
  <meta charset='utf-8'>
  <meta name='viewport' content='width=device-width'>
  <title>LitElement w/ Tailwind</title>

  <style>
    body {
      font-family: 'Helvetica Neue', Arial, sans-serif;
      color: #333;
      font-weight: 300;
    }
  </style>
</head>

<body>
  <app-component></app-component>
  <script type="module" src='bundle.js'></script>
</body>

</html>
```

You'll most likely run into several ESLint and TypeScript issues. A few to be wary of are:

- **TypeScript Decorators** : The line with `@customElement()` is a TypeScript feature called a [decorator](https://www.typescriptlang.org/docs/handbook/decorators.html). In order to use them, you have to explicitly turn the feature in in your `tsconfig.json`.
- **Expected 'this' to be used by class method 'render'** : This is an ESLint rule noticing that we aren't using `this` in the instance method `render()`. We can tell ESLint to ignore specific functions by adding a rule to our `eslintrc.js`

After resolving any linting issues you may have, you should have your Hello, World!

[![Hello, World w/ LitElement](https://images.ctfassets.net/zojzzdop0fzx/7wQxqo0sqTxfyTWj1zLzzA/957d7976f6e2065b0269220d8746d074/hello-world-lit.png)](https://images.ctfassets.net/zojzzdop0fzx/7wQxqo0sqTxfyTWj1zLzzA/957d7976f6e2065b0269220d8746d074/hello-world-lit.png)

## Styling components with Tailwind

Now we are going to add Tailwind to our project.

Install the following dependencies:

- `rollup-plugin-postcss`
- `tailwindcss`
- `postcss`
- `autoprefixer`

Run the following command to generate a Tailwind configuration file:

`npx tailwindcss init`

`tailwindcss.config.js`

```
module.exports = {
  purge: [
    './src/**/*.html',
    './src/**/*.js',
  ],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
```

Create/Edit the following files:

`rollup.config.js`

```
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
import typescript from '@rollup/plugin-typescript';
import { babel } from '@rollup/plugin-babel'
import postcss from 'rollup-plugin-postcss' // import postcss plugin

const production = !process.env.ROLLUP_WATCH;

export default {
  input: 'src/main.ts',
  output: {
    file: 'public/bundle.js',
    format: 'es',
    sourcemap: true
  },
  plugins: [
    resolve(),
    commonjs(),
    postcss(), // use postcss plugin
    typescript({
      include: ['src/**/*.ts']
    }),
    babel({
      babelHelpers: 'bundled',
      exclude: "node_modules/**"
    }),
    production && terser({ format: { comments: false } }),
  ]
};
```

`postcss.config.js`

```
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}
```

`src/styles.css`

```
@tailwind base;
@tailwind components;
@tailwind utilities;
```

`src/main.ts`

```
import './styles.css' // import the tailwind styles
import { LitElement, customElement, html } from "lit-element";

@customElement("app-component")
export default class AppComponent extends LitElement {
  createRenderRoot() {
    return this; // turn off shadow dom to access external styles
  }
  render() {
    return html` <h1 class="mx-auto my-4 py-4 text-center shadow-lg text-xl w-1/2">Hello, World!</h1> `; // use tailwind css utility classes
  }
}
```

After our configuration, we now have a working LitElement & Tailwind setup!

[![Styled with Tailwind](https://images.ctfassets.net/zojzzdop0fzx/5Iq9TmexlLk8kWO3bnpemq/dc77e0069b1aca1744b60ad91acdd643/tailwind-working.png)](https://images.ctfassets.net/zojzzdop0fzx/5Iq9TmexlLk8kWO3bnpemq/dc77e0069b1aca1744b60ad91acdd643/tailwind-working.png)

## Conclusion

After our configuration, we now have a strong base to start creating a beautiful data-driven user interface with powered by both LitElement and Tailwind CSS.

_This Dot Labs is a modern web consultancy focused on helping companies realize their digital transformation efforts. For expert architectural guidance, training, or consulting in React, Angular, Vue, Web Components, GraphQL, Node, Bazel, or Polymer, visit [thisdotlabs.com](https://www.thisdotlabs.com)._

_This Dot Media is focused on creating an inclusive and educational web for all. We keep you up to date with advancements in the modern web through events, podcasts, and free content. To learn, visit [thisdot.co](https://www.thisdot.co)._