Prerequisites
Before starting the project make sure to install the following utilities:
Creating a new Next application
Let's create a fresh Next application so that we can go through all the steps together.
Step 1
Create new project
npx create-next-app@latest
cd my-project
Installing and configuring Tailwind CSS and Tailwind Elements
If you have created a new next app, depending on the chosen options, your file structure could look like this:
Yes
, ESLint - Yes
, `src/` directory - Yes
, App Router - both examples included in this tutorial
my-project/
├── node_modules/
├── public/
├── src/
│ ├── pages/ - needs to be created
│ ├── app/
│ │ ├── favicon.ico
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── index.css
│ ├── ...
│ └── index.js
├── package-lock.json
├── package.json
├── next.config.js
├── tsconfig.config.js
├── ...
└── tailwind.config.js
my-project/
├── node_modules/
├── public/
├── src/
│ ├── pages/
│ ├──── app/
│ │ ├── _app.tsx
│ │ ├── _document.tsx
│ │ └── index.tsx
│ └── styles/
│ └── globals.css
├── package-lock.json
├── package.json
├── next.config.js
├── tsconfig.config.js
├── ...
└── tailwind.config.js
Step 1
Install Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Step 2
Add the paths to all of your template files in your
tailwind.config.cjs
file.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
"./node_modules/tw-elements/dist/js/**/*.js",
// Or if using `src` directory:
"./src/**/*.{js,ts,jsx,tsx}",
"./node_modules/tw-elements/dist/js/**/*.js"
],
theme: {
extend: {},
},
darkMode: "class",
plugins: [require("tw-elements/dist/plugin.cjs")]
}
Step 3
Add the @tailwind
directives for each of Tailwind's layers to
your ./src/app/globals.css
or ./src/styles/globals.css
file.
@tailwind base;
@tailwind components;
@tailwind utilities;
Step 4
Attach the Roboto
font in:
-
./src/app/layout.tsx
- if chosenApp Router
-
./src/pages/_app.tsx
- without routing
import "./globals.css";
import { Roboto } from "next/font/google";
const roboto = Roboto({ weight: "400", subsets: ["latin"] });
export const metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={roboto.className}>{children}</body>
</html>
);
}
import "@/styles/globals.css";
import type { AppProps } from "next/app";
import { Roboto } from "next/font/google";
import "tw-elements/dist/css/tw-elements.min.css";
const roboto = Roboto({ weight: "400", subsets: ["latin"] });
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<style jsx global>{`
html {
font-family: ${roboto.style.fontFamily};
}
`}</style>
<Component {...pageProps} />
</>
);
}
Step 5
Install Tailwind Elements.
npm install tw-elements
Step 6
Import globally in ./src/app/layout.tsx
or ./src/pages/_app.tsx
Tailwind Elements CSS file (you could do it also in specific component which are using TE).
import "tw-elements/dist/css/tw-elements.min.css";
Step 7
Create standalone file with name of your component (for example MyComponent.js
in src/pages
directory - create one if it doesn't exist) and dynamic import TE components which are you intend to use. Also include necessary function initTE
. Initialize initTE
in a lifecycle method. Since tw-elements
needs to be used client side
don't forget to add "client side"
at the beginning of the file.
"use client";
import { useEffect } from "react";
const MyComponent = () => {
useEffect(() => {
const init = async () => {
const { Datepicker, Input, initTE } = await import("tw-elements");
initTE({ Datepicker, Input });
};
init();
}, []);
return (
<div
className="relative mb-3"
data-te-datepicker-init
data-te-input-wrapper-init
>
<input
type="text"
className="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[te-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary [&:not([data-te-input-placeholder-active])]:placeholder:opacity-0"
placeholder="Select a date"
/>
<label
htmlFor="floatingInput"
className="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>
Select a date
</label>
</div>
);
};
export default MyComponent;
Step 8
Import the newly created component wrapping it into dynamic
import with ssr
object set to false
. You can do it either in:
./src/app/page.tsx
- or
./src/pages/index.tsx
import dynamic from "next/dynamic";
const DynamicComponent = dynamic(() => import("../pages/MyComponent"), {
ssr: false,
});
const Home = () => {
return (
<>
<DynamicComponent />
</>
);
};
export default Home;
Step 9
Start the app and see if everything's fine
npm run dev
Initializing via JS
By default all components have autoinit which means they are initialized by data attributes. But if you want to make init by JavaScript - there is also possibility to do that.
Step 1
Import components which are you intend to use and initialize components in lifecycle hook.
import { useEffect } from "react";
const MyComponent = () => {
useEffect(() => {
const init = async () => {
const { Datepicker, Input, initTE } = await import("tw-elements");
const myInput = new Input(document.getElementById("myDatepicker"));
const options = {
format: "dd-mm-yyyy",
};
const myDatepicker = new Datepicker(
document.getElementById("myDatepicker"),
options
);
}
init();
}, []);
return (
<div
className="relative mb-3"
id="myDatepicker"
>
<input
type="text"
className="peer block min-h-[auto] w-full rounded border-0 bg-transparent px-3 py-[0.32rem] leading-[1.6] outline-none transition-all duration-200 ease-linear focus:placeholder:opacity-100 peer-focus:text-primary data-[te-input-state-active]:placeholder:opacity-100 motion-reduce:transition-none dark:text-neutral-200 dark:placeholder:text-neutral-200 dark:peer-focus:text-primary [&:not([data-te-input-placeholder-active])]:placeholder:opacity-0"
placeholder="Select a date"
/>
<label
htmlFor="floatingInput"
className="pointer-events-none absolute left-3 top-0 mb-0 max-w-[90%] origin-[0_0] truncate pt-[0.37rem] leading-[1.6] text-neutral-500 transition-all duration-200 ease-out peer-focus:-translate-y-[0.9rem] peer-focus:scale-[0.8] peer-focus:text-primary peer-data-[te-input-state-active]:-translate-y-[0.9rem] peer-data-[te-input-state-active]:scale-[0.8] motion-reduce:transition-none dark:text-neutral-200 dark:peer-focus:text-primary"
>
Select a date
</label>
</div>
);
};
export default MyComponent;