Nuxt UI is a library that allows us to build scalable interfaces with Nuxt without the need to build from scratch. In this tutorial, we’ll be building a minimal X (Twitter) profile UI using the Nuxt UI component library.
Nuxt UI was created using technologies like Headless UI and Tailwind CSS. As a result, you’ll essentially be working with these two technologies while building with Nuxt UI, enabling developers to implement projects faster without the need to start from scratch.
Building with Nuxt UI
During the development process, we will be utilizing specific components such as tabs, buttons, containers, cards, and more from the Nuxt UI component library. This allows us to avoid building these components from scratch. Additionally, Nuxt UI uses Tailwind CSS classes to handle the styling, which comes prepackaged with the Nuxt UI module.
By the end of this tutorial, we will have build this demo:
Here’s the source code for this project.
Setting up our X (Twitter) clone project
To get started, we’ll create a new Nuxt 3 project using this command:
npx nuxi@latest init twitter-clone
Then we’ll navigate into our project’s directory and run our server using these commands:
cd twitter-clone
npm run dev
We should see the initial Nuxt 3 starter UI in our browser:
Once this is done, we can now install our Nuxt UI library using this command:
npm install @nuxt/ui
Now, in our nuxt.config.ts
file, we’ll navigate to the modules section and add @nuxt/ui
to the array so we have access to its features in our project.
export default defineNuxtConfig({
modules: ['@nuxt/ui']
})
Building the App’s UI
Now that we have our project ready with Nuxt UI set up to be used, we can start building out the UI.
We’ll go into the components folder and create a new component named Twitter.vue
. This is where we’ll be building out our X (Twitter) profile UI clone.
Then we’ll add the <Twitter/>
component in our app.vue
as a child.
Now this component is ready to be styled. As mentioned earlier, Nuxt UI comes pre-packaged with Tailwind CSS, so we can use a couple classes from that library here.
We will add bg-[#000101]
to set a dark background color, and h-screen
to make the site’s height match the size of the viewport.
<template>
<Twitter class="bg-[#000101] h-screen" />
</template>
Adding Images using Nuxt Image
In every X (Twitter) profile page, there is a header image and a profile image (also known as the avatar). In order to add these images to our Nuxt project, we’ll use Nuxt’s official image module, Nuxt Image.
Nuxt Image automatically optimizes images in our Nuxt apps and comes with impressive features such as dynamically generating responsive sizes for your images.
Let’s install the module by running this command in the terminal:
npm install @nuxt/image@rc
Now, we’ll go into the the nuxt.config.ts
and include it in the modules
array, along with our Nuxt UI module from earlier.
export default defineNuxtConfig({
modules: ["@nuxt/ui", "@nuxt/image"]
})
Centering the Wrapper
We first need to center our wrapper div
to contain all the UI elements on the profile page. First, we’d set the display of the outer wrapper to grid
and justify the items to the center with justify-center
. Then, we have an inner wrapper with border-x
and border-gray-600
, which creates borders on the two vertical sides of the wrapper. Plus, it defines the color of the borders.
<div class="grid justify-center">
<div class="border-x border-gray-600"></div>
</div>
Adding the header image
To add our header image, we’ll use <NuxtImg/>
to pull in the image (vue-cover.jpeg
), located in our public
folder.
<div class="grid justify-center">
<div class="border-x border-gray-600">
<NuxtImg src="/vue-cover.jpeg" />
</div>
</div>
Now, the image is the first UI component in the wrapper.
<div class="grid justify-center">
<div class="border-x border-gray-600">
<NuxtImg src="/vue-cover.jpeg" />
</div>
</div>
Adding the profile picture
Next, let’s add our second image: the profile picture. For this, we’ll use the Nuxt UI <UAvatar/>
component, and style it with Tailwind CSS classes.
<template>
<div class="grid justify-center">
<div class="border-x border-gray-600">
<NuxtImg src="/vue-cover.jpeg" />
<div class="flex mx-4">
<UAvatar
class="-mt-16"
src="/logo.jpeg"
alt="Profile Avatar"
size="xl"
/>
</div>
</div>
</div>
</template>
Let’s now wrap the <UAvatar/>
in a <div/>
. The display of this <div/>
is defined as flex
. While, mx-4
to add a bit of margin to both sides.
The avatar component comes with its own size
prop, with assigned values for height, width and text sizes (as a fallback for avatars without images) which are styled using Tailwind CSS classes.
You may decide to use the default size preset (sm
, md
, lg
, xl
) included in the component, but if you’d like to override the default values with your own custom Tailwind CSS classes, you can do that by editing the app.config.ts
file, which should be in the root folder of your app.
Since we do want to override the default values, we’ll go into our app.config.ts
file and update the xl
values to be "h-32 w-32"
.
export default defineAppConfig({
ui: {
strategy: "override",
primary: "cool",
avatar: {
size: {
xl: "h-32 w-32",
},
}
},
});
These Tailwind classes give us an image that is larger than the default xl
preset was.
Our next step is to add the Nuxt UI <UButton>
component for our “Edit Profile” button.
<template>
<div class="grid justify-center">
<div class="border-x border-gray-600">
<NuxtImg src="/vue-cover.jpeg" />
<div class="flex mx-4">
<UAvatar
class="-mt-16"
src="/logo.jpeg"
alt="Profile Avatar"
size="xl"
/>
<UButton
:ui="{ rounded: 'rounded-full' }"
color="gray"
variant="outline"
class="ml-auto h-min mt-3"
>Edit Profile</UButton
>
</div>
</div>
</div>
</template>
We’ll add certain props to the <UButton>
component. Let’s set the color
prop as gray
and variant
to outline
. We then add our own margin class styles "ml-auto h-min mt-3"
to position the button to the right end and add a bit of margin to the top.
Finally, we have the the :ui
prop that allows us overwrite the default value of the other props directly within the component. In this case, we’re overwriting the the rounded
prop with rounded-full
.
With all this in place, we should have this displayed in our local app:
Building the Profile Bio section
Next up is our bio section. This is quite straightforward as it contains only text. Let’s add the following code to our Twitter.vue
component:
<template>
<div class="grid justify-center">
<div class="border-x border-gray-600">
<NuxtImg src="/vue-cover.jpeg" />
<div class="flex mx-4">
<UAvatar class="-mt-16" src="/logo.jpeg"
alt="Profile Avatar" size="xl" />
<UButton :ui="{ rounded: 'rounded-full' }"
color="gray" variant="outline"
class="ml-auto h-min mt-3">
Edit Profile
</UButton>
</div>
<!-- Profile bio section -->
<div class="px-4">
<h3 class="text-xl font-bold">Vue Mastery</h3>
<span class="text-[#686E73] mb-3">@VueMastery</span>
<p class="mb-6">
The ultimate learning platform for Vue.js developers
</p>
<p
>Learn from the experts,<br />Elevate your code, <br />Master your
craft</p
>
<a href="vuemastery.com/courses" class="text-[#2C9AF0]">vuemastery.com/courses</a>
<a class="flex my-3 items-center text-[#2C9AF0]" href="vuemastery.com/courses">
<svg viewBox="0 0 24 24" aria-hidden="true" class="h-5 w-5 mr-1 fill-current text-[#71777B]">
<g>
<path
d="M18.36 5.64c-1.95-1.96-5.11-1.96-7.07 0L9.88 7.05 8.46 5.64l1.42-1.42c2.73-2.73 7.16-2.73 9.9 0 2.73 2.74 2.73 7.17 0 9.9l-1.42 1.42-1.41-1.42 1.41-1.41c1.96-1.96 1.96-5.12 0-7.07zm-2.12 3.53l-7.07 7.07-1.41-1.41 7.07-7.07 1.41 1.41zm-12.02.71l1.42-1.42 1.41 1.42-1.41 1.41c-1.96 1.96-1.96 5.12 0 7.07 1.95 1.96 5.11 1.96 7.07 0l1.41-1.41 1.42 1.41-1.42 1.42c-2.73 2.73-7.16 2.73-9.9 0-2.73-2.74-2.73-7.17 0-9.9z">
</path>
</g>
</svg>
<span>vuemastery.com/courses</span>
</a>
</div>
</div>
</div>
</template>
Under the Profile bio section above, we have all the necessary texts.
This starts with the profile name and X(twitter) handle. The name is bold with the font styles: "text-xl font-bold"
. Then, the handle has a color of text-[#686E73]
with a bit of margin on top to separate it from the rest of the bio texts below using mb-3
.
Then, we have the bio texts that are separated from each other with margins at the top and bottom. The final part of the bio contains a link just before the main link section. It has margins on top and the bottom using mt-6 mb-3
, and we define the color of the link text using text-[#2C9AF0]
Finally, we have the the link section. It consists of the SVG icon and the text. The SVG contains CSS styles describing its size (height and width) using *class*="h-5 w-5"
. After which, we add some margin to the text and customize the color of the icon using mr-1 fill-current text-[#71777B]
.
This will result in:
Adding Tabs to our X (Twitter) Profile
Firstly, let’s go back to our app.config.ts
to add custom styles to our container, card and tabs.
export default defineAppConfig({
ui: {
strategy: "override",
primary: "cool",
avatar: {
size: {
xl: "h-32 w-32",
},
},
card: {
background: "bg-transparent",
divide: "divide-none",
ring: "ring-0 ",
base: "border-b border-gray-600 ",
rounded: "rounded-none",
},
tabs: {
list: {
rounded: "rounded-none",
background: "bg-transparent",
base: "border-b border-gray-600",
marker: {
base: "w-full h-full",
background: "bg-transparent",
}
},
},
},
container: {
padding: "pa-0",
},
},
});
The padding of our container
component is set to 0
using pa-0
. For our card
, we’re setting the background to be transparent. Next, we remove the border, the outline and border-radius with divide-none
,ring-0
, and rounded-none
respectively. We then add our preferred border with "border-b border-gray-600"
.
Similarly, for Tabs
we’re defining the border-radius with rounded-none
, setting the background color to bg-transparent
, and setting the marker
’s base
to 100% width
and height
using w-full h-full
. We also add border at the bottom with border-b border-gray-600
.
Back on our X profile page, there is a tabs section that has Posts and Replies. So let’s build that out now.
Inside the <script/>
tag of our Twitter.vue component, we will add three arrays:
- The
items
array will store the number of items in our tabs. - The
posts
array will contain the content of each individual post. - The
replies
array will hold the content of each individual reply.
To give our demo UI some content to display inside of these tabs, we’ll be adding in some hard-coded data.
Twitter.vue
<script setup>
const items = [
{
key: "posts",
label: "Posts"
},
{
key: "replies",
label: "Replies"
},
];
const posts = [
{
content:
"Our latest course gets you up-and-running with the modern @vitest_dev 🧪",
},
{
content: "New Nuxt UI content coming 🔜 🤓",
},
];
const replies = [
{
content:
"The Nuxt 3 DevTools are packed full of features. Are you using them to their fullest potential?",
},
{
content: "Are you using the Nuxt 3 DevTools to their full potential?",
},
];
</script>
This content will be presented as tabs using Nuxt UI’s <UTabs/>
component.
<UTabs :items="items">
<template #default="{ item, selected }">
<div
v-if="selected"
class="flex items-center align-center gap-2 relative border-b-4 border-[#2C9AF0]"
>
<span class="m-2 align-center"> {{ item.label }}</span>
</div>
</template>
<template #item="{ item }"></template>
</UTabs>
<UTabs/>
loops through our items
array to display our tabs. We have only two tabs: Posts
and Replies
.
Inside <UTabs/>
, we have two <template/>
components. The first template
displays each individual tab item and recognises when the tab is selected
by the user. Here, we define the styles for a selected
tab by specifying this with the v-if="selected"
and the class
styles "flex items-center align-center gap-2 relative border-b-4 border-[#2C9AF0]"
. The most distinctive part of the selected
classes is the bottom border.
We then add the actual label of each tab
and align it to the center with "m-2 align-center"
.
The second <template/>
contains both tabs’ content.
<template #item="{ item }"></template>
Each tab’s content will be wrapped in <UContainer>
. We’ll conditionally display the list of posts or replies using the v-if
and v-else-if
conditionals.
We’ll use v-if="item*.*key === 'posts'"
to display the list of posts:
<UContainer v-if="item.key === 'posts'">
And v-else-if="item*.*key === 'replies'"
to display the list of replies:
<UContainer v-else-if="item.key === 'replies'">
Inside of the container, we’ll use v-for
to loop over a div
of the posts/replies array to display each item, respectively.
<div v-for="(post, index) in posts" :key="{ index }" class="space-y-3">
As we print them out, we’re using the <UCard/>
Nuxt UI component to display each item.
<UCard
:ui="{
body: {
padding: 'py-0',
},
}"
>
Within the <UCard/>
component, let’s add py-2
for the class styles. Then, there’s the :ui
prop that overrides the component’s default styles. In the case, it’s the padding
prop that’s set to py-0
.
<UCard
:ui="{
body: {
padding: 'py-0',
},
}"
>
<div class="flex w-full"></div>
</UCard>
Next, let’s add more UI components inside our <UCard/>
. To begin with create a div
wrapper and define it’s display as flex
and width as w-full
.
Each post has an avatar that we’d add using <UAvatar/>
. We’ll use <UAvatar/>
to pull in the image (logo.jpeg
), located in our public
folder. Then, add some margin to the left using the class ml-4
<UCard
:ui="{
body: {
padding: 'py-0',
},
}"
>
<div class="flex w-full">
<UAvatar class="ml-4" src="/logo.jpeg" alt="Avatar" size="md" />
</div>
</UCard>
Next, we’d add our name, X(twitter) handle, and it’s content. To achieve the correct styling, there’s a bit of margin to the left (ml-3
). Then, we add an inner div
with flex
and items-baseline
; this allows the name and twitter handle be side-by-side at the bottom of the div
.
<div class="flex w-full">
<UAvatar class="ml-4" src="/logo.jpeg" alt="Avatar" size="md" />
<div class="ml-3 w-full">
<div class="flex items-baseline">
<h3 class="text-base font-bold">Vue Mastery</h3>
<span class="text-[#686E73] text-sm ml-2">@VueMastery</<span>
</div>
<p>{{ post.content }}</p>
</div>
</div>
Then, we have the post.content
to display the content of the post
.
Each post should look just like this:
The final piece to our work are the likes, comments, shares, and views icons.
Let’s create a wrapper div
that’s styled to display flex
. Here, we’d add justify-between
to ensure that our icons and their count are well spaced. While mt-3
helps ensure padding on the top.
Each icon and their count is wrapped around a div
. We add the text color with text-[#71777B]
, define the display as flex
, and center it’s children with items-center
.
The SVG itself has CSS classes to set the height, spacing, and fill it’s color with "h-5 w-5 mr-1 fill-current"
. The count component has a little spacing between itself and the icon and it’s added as class="ml-2"
.
<div class="flex w-full justify-between mt-3">
<div class="flex items-center text-[#71777B]">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 fill-current"
>
<g>
<path
d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z"
></path>
</g>
</svg>
<h3 class="ml-2">2</h3>
</div>
<div class="flex items-center text-[#71777B]">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 fill-current"
>
<g>
<path
d="M4.5 3.88l4.432 4.14-1.364 1.46L5.5 7.55V16c0 1.1.896 2 2 2H13v2H7.5c-2.209 0-4-1.79-4-4V7.55L1.432 9.48.068 8.02 4.5 3.88zM16.5 6H11V4h5.5c2.209 0 4 1.79 4 4v8.45l2.068-1.93 1.364 1.46-4.432 4.14-4.432-4.14 1.364-1.46 2.068 1.93V8c0-1.1-.896-2-2-2z"
></path>
</g>
</svg>
<h3 class="ml-2">1</h3>
</div>
<div class="flex items-center text-[#71777B]">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 fill-current"
>
<g>
<path
d="M16.697 5.5c-1.222-.06-2.679.51-3.89 2.16l-.805 1.09-.806-1.09C9.984 6.01 8.526 5.44 7.304 5.5c-1.243.07-2.349.78-2.91 1.91-.552 1.12-.633 2.78.479 4.82 1.074 1.97 3.257 4.27 7.129 6.61 3.87-2.34 6.052-4.64 7.126-6.61 1.111-2.04 1.03-3.7.477-4.82-.561-1.13-1.666-1.84-2.908-1.91zm4.187 7.69c-1.351 2.48-4.001 5.12-8.379 7.67l-.503.3-.504-.3c-4.379-2.55-7.029-5.19-8.382-7.67-1.36-2.5-1.41-4.86-.514-6.67.887-1.79 2.647-2.91 4.601-3.01 1.651-.09 3.368.56 4.798 2.01 1.429-1.45 3.146-2.1 4.796-2.01 1.954.1 3.714 1.22 4.601 3.01.896 1.81.846 4.17-.514 6.67z"
></path>
</g>
</svg>
<h3 class="ml-2">1</h3>
</div>
<div class="flex items-center text-[#71777B]">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 fill-current"
>
<g>
<path
d="M8.75 21V3h2v18h-2zM18 21V8.5h2V21h-2zM4 21l.004-10h2L6 21H4zm9.248 0v-7h2v7h-2z"
></path>
</g>
</svg>
<h3 class="ml-2">100</h3>
</div>
<div class="flex items-center pr-3">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 rounded-full fill-current text-[#71777B]"
>
<g>
<path
d="M12 2.59l5.7 5.7-1.41 1.42L13 6.41V16h-2V6.41l-3.3 3.3-1.41-1.42L12 2.59zM21 15l-.02 3.51c0 1.38-1.12 2.49-2.5 2.49H5.5C4.11 21 3 19.88 3 18.5V15h2v3.5c0 .28.22.5.5.5h12.98c.28 0 .5-.22.5-.5L19 15h2z"
></path>
</g>
</svg>
</div>
</div>
One thing to note is that the last div
containing the share
SVG has an extra padding to the right (pr-3
) to add extra spacing to the edge.
We’ve successfully displayed our Posts
.
As mentioned above, Replies
basically duplicates the same thing we’ve done for our Posts
using the replies
array. In the spirit of the DRY (Don’t Repeat Yourself) principle, let’s have everything inside another component and import it into our own Twitter.vue
component.
Create a new component inside our components
folder. Lets’s call it **Tweets.vue**
.
// Tweets.vue
<script setup>
const props = defineProps({
items: {
type: Array,
required: true,
},
});
const { items } = props;
</script>
<template>
<UContainer>
<div :key="{ index }" v-for="(item, index) in items" class="space-y-3">
<UCard
class="py-2"
:ui="{
body: {
padding: 'py-0',
},
}"
>
<div class="flex w-full">
<UAvatar class="ml-4" src="/logo.jpeg" alt="Avatar" size="md" />
<div class="ml-3 w-full">
<div class="flex items-baseline">
<h3 class="text-base font-bold">Vue Mastery</h3>
<h6 class="text-[#686E73] text-sm ml-2">@VueMastery</h6>
</div>
<h5>{{ item.content }}</h5>
<div class="flex w-full justify-between mt-3">
<div class="flex items-center">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 rounded-full fill-current text-[#71777B]"
>
<g>
<path
d="M1.751 10c0-4.42 3.584-8 8.005-8h4.366c4.49 0 8.129 3.64 8.129 8.13 0 2.96-1.607 5.68-4.196 7.11l-8.054 4.46v-3.69h-.067c-4.49.1-8.183-3.51-8.183-8.01zm8.005-6c-3.317 0-6.005 2.69-6.005 6 0 3.37 2.77 6.08 6.138 6.01l.351-.01h1.761v2.3l5.087-2.81c1.951-1.08 3.163-3.13 3.163-5.36 0-3.39-2.744-6.13-6.129-6.13H9.756z"
></path>
</g>
</svg>
<h3 class="ml-2 text-[#71777B]">2</h3>
</div>
<div class="flex items-center">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 rounded-full fill-current text-[#71777B]"
>
<g>
<path
d="M4.5 3.88l4.432 4.14-1.364 1.46L5.5 7.55V16c0 1.1.896 2 2 2H13v2H7.5c-2.209 0-4-1.79-4-4V7.55L1.432 9.48.068 8.02 4.5 3.88zM16.5 6H11V4h5.5c2.209 0 4 1.79 4 4v8.45l2.068-1.93 1.364 1.46-4.432 4.14-4.432-4.14 1.364-1.46 2.068 1.93V8c0-1.1-.896-2-2-2z"
></path>
</g>
</svg>
<h3 class="ml-2 text-[#71777B]">1</h3>
</div>
<div class="flex items-center">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 rounded-full fill-current text-[#71777B]"
>
<g>
<path
d="M16.697 5.5c-1.222-.06-2.679.51-3.89 2.16l-.805 1.09-.806-1.09C9.984 6.01 8.526 5.44 7.304 5.5c-1.243.07-2.349.78-2.91 1.91-.552 1.12-.633 2.78.479 4.82 1.074 1.97 3.257 4.27 7.129 6.61 3.87-2.34 6.052-4.64 7.126-6.61 1.111-2.04 1.03-3.7.477-4.82-.561-1.13-1.666-1.84-2.908-1.91zm4.187 7.69c-1.351 2.48-4.001 5.12-8.379 7.67l-.503.3-.504-.3c-4.379-2.55-7.029-5.19-8.382-7.67-1.36-2.5-1.41-4.86-.514-6.67.887-1.79 2.647-2.91 4.601-3.01 1.651-.09 3.368.56 4.798 2.01 1.429-1.45 3.146-2.1 4.796-2.01 1.954.1 3.714 1.22 4.601 3.01.896 1.81.846 4.17-.514 6.67z"
></path>
</g>
</svg>
<h3 class="ml-2 text-[#71777B]">1</h3>
</div>
<div class="flex items-center">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 rounded-full fill-current text-[#71777B]"
>
<g>
<path
d="M8.75 21V3h2v18h-2zM18 21V8.5h2V21h-2zM4 21l.004-10h2L6 21H4zm9.248 0v-7h2v7h-2z"
></path>
</g>
</svg>
<h3 class="ml-2 text-[#71777B]">100</h3>
</div>
<div class="flex items-center pr-3">
<svg
viewBox="0 0 24 24"
aria-hidden="true"
class="h-5 w-5 mr-1 rounded-full fill-current text-[#71777B]"
>
<g>
<path
d="M12 2.59l5.7 5.7-1.41 1.42L13 6.41V16h-2V6.41l-3.3 3.3-1.41-1.42L12 2.59zM21 15l-.02 3.51c0 1.38-1.12 2.49-2.5 2.49H5.5C4.11 21 3 19.88 3 18.5V15h2v3.5c0 .28.22.5.5.5h12.98c.28 0 .5-.22.5-.5L19 15h2z"
></path>
</g>
</svg>
</div>
</div>
</div>
</div>
</UCard>
</div>
</UContainer>
</template>
We create an items
prop that we’d use to pass in either the posts
or replies
array from our Twitter.vue
. We then move the entire <UContainer/>
into this component. This loops over the items
array prop at every point in time.
Go back into the Twitter.vue
component and replace the entire <UTabs/>
component with this below:
<UTabs :items="items">
<template #default="{ item, selected }">
<div
v-if="selected"
class="flex items-center align-center gap-2 relative border-b-4 border-[#2C9AF0]"
>
<span class="m-2 align-center"> {{ item.label }}</span>
</div>
</template>
<template #item="{ item }">
<Tweets v-if="item.key === 'posts'" :items="posts" />
<Tweets v-else-if="item.key === 'replies'" :items="replies" />
</template>
</UTabs>
All we’ve done here is to replace the both <UContainer/>
components with the <Tweets/>
components. This now works perfectly for both Posts
and Replies
.
Here’s the final version of our demo:
Continue Learning
Congratulations on completing this tutorial. We covered the steps you’ll need to use Nuxt UI in your project. From installation to initialization with the correct config, and finally using the components within our project.
You can checkout the official Nuxt UI documentation to explore the powerful features of Nuxt UI and incorporate the various components within your own projects. To continue deepening your understanding of the Nuxt ecosystem, check out the courses recommended below.