<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Archives des Web - dbi Blog</title>
	<atom:link href="https://www.dbi-services.com/blog/category/web/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dbi-services.com/blog/category/web/</link>
	<description></description>
	<lastBuildDate>Mon, 15 Dec 2025 12:37:59 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/cropped-favicon_512x512px-min-32x32.png</url>
	<title>Archives des Web - dbi Blog</title>
	<link>https://www.dbi-services.com/blog/category/web/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>AI Isn’t Your Architect: Real-World Issues in a Vue  project</title>
		<link>https://www.dbi-services.com/blog/ai-isnt-your-architect-real-world-issues-in-a-vue-project/</link>
					<comments>https://www.dbi-services.com/blog/ai-isnt-your-architect-real-world-issues-in-a-vue-project/#respond</comments>
		
		<dc:creator><![CDATA[Nicolas Meunier]]></dc:creator>
		<pubDate>Mon, 15 Dec 2025 12:37:57 +0000</pubDate>
				<category><![CDATA[Development & Performance]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[AI Agent]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[NestJS]]></category>
		<category><![CDATA[Vue3]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=42009</guid>

					<description><![CDATA[<p>In my previous article I generated a Rest NestJS API using AI. Today, I will create a small UI to authenticate users via the API. I will use this simple case to show the limits of coding with AI and what you need to be attentive to. I will create my interface with Vue 3 [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/ai-isnt-your-architect-real-world-issues-in-a-vue-project/">AI Isn’t Your Architect: Real-World Issues in a Vue  project</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In my <a href="https://www.dbi-services.com/blog/how-effective-is-ai-on-a-development-project/">previous article</a> I generated a Rest NestJS API using AI. <br>Today, I will create a small UI to authenticate users via the API. I will use this simple case to show the limits of coding with AI and what you need to be attentive to.</p>



<p class="wp-block-paragraph">I will create my interface with <a href="https://vuejs.org/">Vue 3</a> and <a href="https://vuetifyjs.com/en/">Vuetify</a> still using the <a href="https://github.com/features/copilot">GitHub Copilot</a> agent on Vs Code.</p>



<h2 class="wp-block-heading" id="h-initializing-the-project">Initializing the project</h2>



<p class="wp-block-paragraph">I create the new Vuetify project with the npm command:</p>



<pre class="wp-block-code"><code>npm create vuetify@latest</code></pre>



<p class="wp-block-paragraph">To avoid CORS request between the Vuetify project and the API project, I&#8217;m configuring a proxy into Vite like in <a href="https://www.dbi-services.com/blog/avoid-cors-requests-in-development-mode-with-vite/">my other article</a>.</p>



<p class="wp-block-paragraph">In the AI chat, I also initialize my context</p>



<pre class="wp-block-code"><code>Remember:
- You are a full-stack TypeScript developer.
- You follow best practices in development and security.
- You will work on this NestJS project.</code></pre>



<p class="wp-block-paragraph">To guide the AI, I&#8217;m exporting the Open API definition into a file in my project: /api-docs/open-api.json</p>



<h2 class="wp-block-heading" id="h-connecting-to-api-first-issue">Connecting to API, first issue</h2>



<p class="wp-block-paragraph">First, I want to connect my UI to the API, and I ask the AI the following:</p>



<pre class="wp-block-code"><code>Connect the application to the API. The API url path is /api</code></pre>



<p class="wp-block-paragraph">The result is not what I expected… My goal was to generate a simple class that makes requests to API with support for JWT tokens, but by default the AI wanted to add the Axios library to the project.</p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="563" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/AI_add_axios-1024x563.png" alt="" class="wp-image-42011" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/AI_add_axios-1024x563.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/AI_add_axios-300x165.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/AI_add_axios-768x422.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/AI_add_axios-1536x845.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/AI_add_axios-2048x1126.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">I’m not saying that Axios is a bad library, but it’s far too complicated for my usage and will add too many dependencies to the project, and therefore more maintenance.</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="517" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Axios_npm-1-1024x517.png" alt="" class="wp-image-42013" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Axios_npm-1-1024x517.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Axios_npm-1-300x151.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Axios_npm-1-768x388.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Axios_npm-1-1536x776.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Axios_npm-1-2048x1034.png 2048w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">So I&#8217;m skipping the installation of the library and I&#8217;m stopping the AI agent.</p>



<p class="wp-block-paragraph">To continue and generate the desired code, I ask the AI:</p>



<pre class="wp-block-code"><code>I don't want to use axios, connect the application to the API with native typescript code</code></pre>



<p class="wp-block-paragraph"> With this prompt, the generated code is fine.</p>



<h2 class="wp-block-heading" id="h-authentication-service-hidden-issue">Authentication Service, hidden issue</h2>



<p class="wp-block-paragraph">Without going into the details, I asked the AI to create my authentication form and the associated service:</p>



<pre class="wp-block-code"><code>Add a page /login to authenticate users, Use vuetify for login form.
Add a service to authenticate the users using the api endpoint /auth/login
The api return jwt token.
When the user is authenticated, redirect the user to /home
If a user accesses /index without authentication redirect the user to /login</code></pre>



<p class="wp-block-paragraph">The result looks good and works:</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="550" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Login_Form-1-1024x550.png" alt="" class="wp-image-42015" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Login_Form-1-1024x550.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Login_Form-1-300x161.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Login_Form-1-768x412.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Login_Form-1.png 1222w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">At first glance, the code works and I can authenticate myself. But the problem comes from the code itself:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="569" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Code_Storage-1024x569.png" alt="" class="wp-image-42016" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Code_Storage-1024x569.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Code_Storage-300x167.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Code_Storage-768x427.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Code_Storage-1536x854.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Code_Storage.png 1730w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">The localStorage is accessible by all scripts, thus vulnerable to XSS attacks.</p>



<p class="wp-block-paragraph">JWT access tokens should not be stored in persistent storage accessible by JavaScript, such as localStorage. To reduce the risk of XSS attacks, it is preferable to store the access token in a Vue service variable rather than in persistent browser storage.</p>



<p class="wp-block-paragraph"><strong>Note:</strong> When stored in memory, the token will be lost at every page refresh, which requires implementing a refresh token mechanism. The refresh token should be stored in an <code>HttpOnly</code> cookie, allowing the access token to have a short expiration time and significantly limiting the impact of a potential attack.</p>



<p class="wp-block-paragraph">To solve the issue I asked the AI the following:</p>



<pre class="wp-block-code"><code>Don't use localStorage to store the token, it's a security issue</code></pre>



<p class="wp-block-paragraph">Using GPT5-min, it only does the work:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="569" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/GPT5-mini-1024x569.png" alt="" class="wp-image-42017" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/GPT5-mini-1024x569.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/GPT5-mini-300x167.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/GPT5-mini-768x427.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/GPT5-mini-1536x853.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/GPT5-mini-2048x1138.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">With Claude Haiku 4.5, we have a short notice:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="569" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Claude-1024x569.png" alt="" class="wp-image-42018" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Claude-1024x569.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Claude-300x167.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Claude-768x427.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Claude-1536x853.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Claude-2048x1137.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading" id="h-why-does-this-happen">Why does this happen?</h2>



<p class="wp-block-paragraph">I tried different AI models in GitHub Copilot, but, from GPT to Claude, the result was similar. Most AIs generate code with Axios and localStorage for this use, because they replicate the most common patterns found in their training data, not the most up-to-date or secure practices. </p>



<p class="wp-block-paragraph">Axios is overrepresented in tutorials because it offers a simple, opinionated HTTP abstraction that is easier for an AI to reason about than the lower-level <code>fetch</code> API. </p>



<p class="wp-block-paragraph">The storage of JWT in localStorage is still widely shown online as it reflects old frontend authentication practices that prioritized simplicity over security. It keeps the token easily accessible to JavaScript and avoids the processing of cookies and refresh token rotation. Although largely discouraged today, these examples remain overrepresented in the tutorials and training data used by AI models.</p>



<p class="wp-block-paragraph">In short, AI prioritizes widely recognized patterns and simplicity of implementation over minimalism and real-world security considerations.</p>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p class="wp-block-paragraph">Although AI is an incredible tool that helps us in our development work, it is important to understand the limits of this tool. With AI, the new role of developers is to imagine the code architecture, ask AI, evaluate the result and review the code. As its name indicates very well, &#8220;Copilot&#8221; is your <strong><em>co</em></strong>-pilot, you must remain the pilot.</p>



<p class="wp-block-paragraph">AI can write code, but it does not understand the consequences of architectural decisions.</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/ai-isnt-your-architect-real-world-issues-in-a-vue-project/">AI Isn’t Your Architect: Real-World Issues in a Vue  project</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/ai-isnt-your-architect-real-world-issues-in-a-vue-project/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How effective is AI on a development project?</title>
		<link>https://www.dbi-services.com/blog/how-effective-is-ai-on-a-development-project/</link>
					<comments>https://www.dbi-services.com/blog/how-effective-is-ai-on-a-development-project/#respond</comments>
		
		<dc:creator><![CDATA[Nicolas Meunier]]></dc:creator>
		<pubDate>Fri, 12 Dec 2025 07:49:18 +0000</pubDate>
				<category><![CDATA[Development & Performance]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[NestJS]]></category>
		<category><![CDATA[TypeScript]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=41782</guid>

					<description><![CDATA[<p>In this article, I will try to evaluate the benefits of AI on a development project and what concrete changes it makes to our development practices. The test case and the approach I chose a familiar environment for my comparison: a new NestJS project from scratch. For my project, I want to: To help me, [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-effective-is-ai-on-a-development-project/">How effective is AI on a development project?</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In this article, I will try to evaluate the benefits of AI on a development project and what concrete changes it makes to our development practices.</p>



<h2 class="wp-block-heading" id="h-the-test-case-and-the-approach"><strong>The test case and the approach</strong></h2>



<p class="wp-block-paragraph">I chose a familiar environment for my comparison: a new <a href="https://nestjs.com/">NestJS</a> project from scratch.</p>



<p class="wp-block-paragraph">For my project, I want to:</p>



<ul class="wp-block-list">
<li>Use a .env file for configuration</li>



<li>Connect to a PostgreSQL database</li>



<li>Store users in a database table</li>



<li>Create a CRUD API to manage my users</li>



<li>Manage JWT authentication based on my user list</li>



<li>Secure CRUD routes for authenticated users using a guard</li>
</ul>



<p class="wp-block-paragraph">To help me, I&#8217;m going to use the <a href="https://github.com/features/copilot">GitHub Copilot</a> agent with the GTP5-mini model. I&#8217;ll ask it to generate code on my behalf, as much as possible. However, I&#8217;ll continue to follow NestJS best practices by using the documentation recommendations and initializing the project myself. I will focus on prompting, initializing the context and reviewing the code generated by the AI.</p>



<p class="wp-block-paragraph">For better results, I will develop the application step by step and control the generated code at each step.</p>



<h2 class="wp-block-heading" id="h-intialize-the-project"><strong>Intialize the project</strong></h2>



<p class="wp-block-paragraph">At first, I initialize a new NestJS project using the CLI, as mentioned in the documentation:</p>



<pre class="wp-block-code"><code>npm i -g @nestjs/cli
nest new nestjs-project</code></pre>



<h2 class="wp-block-heading" id="h-first-contact-with-the-ai-agent"><strong>First contact with the AI agent</strong></h2>



<p class="wp-block-paragraph">I start by opening the project in VSCode and I open a new chat with the AI agent. I&#8217;m trying to give it some general instructions for the rest of the tasks:</p>



<pre class="wp-block-code"><code>Remember:
- You are a full-stack TypeScript developer.
- You follow best practices in development and security.
- You will be working on this NestJS project.</code></pre>



<p class="wp-block-paragraph">The AI agent discovers the project:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="574" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/VsCode-1024x574.png" alt="" class="wp-image-41962" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/VsCode-1024x574.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/VsCode-300x168.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/VsCode-768x430.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/VsCode-1536x861.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/VsCode-2048x1148.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading" id="h-first-task-add-application-configuration"><strong>First Task, add application configuration</strong></h2>



<p class="wp-block-paragraph">I followed the documentation to add <a href="https://docs.nestjs.com/techniques/configuration">configuration</a> support using .env files</p>



<p class="wp-block-paragraph">I&#8217;ve manually added the required package:</p>



<pre class="wp-block-code"><code>npm i --save @nestjs/config</code></pre>



<p class="wp-block-paragraph">And asked the AI to generate the code:</p>



<pre class="wp-block-code"><code>@nestjs/config is installed. Add support for .env in the application. The config file must contain the credentials to access to the database (host, database name, user, password).</code></pre>



<h2 class="wp-block-heading" id="h-second-task-connect-to-the-database-and-create-the-users-table"><strong>Second Task, connect to the database and create the users table</strong></h2>



<p class="wp-block-paragraph">I want to use <a href="https://docs.nestjs.com/techniques/database">TypeORM</a> to manage my database connections and migrations.</p>



<p class="wp-block-paragraph">First, I install the required packages:</p>



<pre class="wp-block-code"><code>npm install --save @nestjs/typeorm typeorm pg</code></pre>



<p class="wp-block-paragraph">And then ask the AI agent to generate the code:</p>



<pre class="wp-block-code"><code>I will use typeorm and postgres. Connect the application to the database.
Save the credentials in the .env file.
Use the credentials:
- host: localhost
- name: nestjs, 
- user: nest-user 
- password XXX</code></pre>



<p class="wp-block-paragraph"><strong>Note</strong> : Be careful when you send credentials to AI</p>



<p class="wp-block-paragraph">Next request to the AI agent: create a migration to initialize the database schema:</p>



<pre class="wp-block-code"><code>Add a migration to create a "users" table with the following fields: id (primary key), username (string), email (string), password (string), is_admin (boolean), disabled (boolean), created_at (timestamp), updated_at (timestamp).</code></pre>



<p class="wp-block-paragraph">In addition, in my package.json, the agent adds the migration command to npm in the project:</p>



<pre class="wp-block-code"><code>  "typeorm:migration:run": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run -d ./data-source.ts",</code></pre>



<p class="wp-block-paragraph">To simplify the process, I asked the AI agent to generate a default user for my application:</p>



<pre class="wp-block-code"><code>In the migration, add a default admin user with the following values:
    username: "admin"
    email: "admin@example.com"
    password: "Admin@123" (hash the password using bcrypt)
    is_admin: true
    disabled: false</code></pre>



<p class="wp-block-paragraph">After the completion by the AI agent, I run the migration.</p>



<h2 class="wp-block-heading" id="h-first-module-service-and-controller-for-users-with-crud-endpoints"><strong>First module, service and controller for users with CRUD endpoints</strong></h2>



<p class="wp-block-paragraph">Now, I ask the agent to create the users module with detailed endpoints:</p>



<pre class="wp-block-code"><code>Add a module, service, and controller for "users" with the following endpoints:
- GET /users: Retrieve a list of all users.
- GET /users/:id: Retrieve a user by ID.
- POST /users: Create a new user.
- PUT /users/:id: Update a user by ID.
- DELETE /users/:id: Delete a user by ID.</code></pre>



<p class="wp-block-paragraph">This step is very quick, and the code is generated in 4min only !</p>



<h2 class="wp-block-heading" id="h-add-swagger-documentation"><strong>Add Swagger documentation</strong></h2>



<p class="wp-block-paragraph">To test the first REST module, I ask the AI to add <a href="https://docs.nestjs.com/openapi/introduction">Swagger UI</a> to the project.</p>



<p class="wp-block-paragraph">As with the other steps, I add the packages myself:</p>



<pre class="wp-block-code"><code>npm install --save @nestjs/swagger</code></pre>



<p class="wp-block-paragraph"><strong>Note:</strong> This step is very tricky for the AI, if you don&#8217;t specify the already installed package, it will try to install an outdated version.</p>



<p class="wp-block-paragraph">Then, I ask the AI agent to generate the code:</p>



<pre class="wp-block-code"><code>@nestjs/swagger is installed
Add swagger to the application. 
Document the users endpoints.</code></pre>



<p class="wp-block-paragraph">In few minutes, we have the API documentation:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="610" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-UI-1024x610.png" alt="" class="wp-image-41785" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-UI-1024x610.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-UI-300x179.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-UI-768x457.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-UI-1536x915.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-UI.png 1560w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">During API testing, I noticed that the password hash was returned in the user list. However, initially, I had instructed the AI ​​to follow security best practices&#8230;</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="580" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Password-Hash-1024x580.png" alt="" class="wp-image-41786" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Password-Hash-1024x580.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Password-Hash-300x170.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Password-Hash-768x435.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Password-Hash.png 1523w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">I asked the AI agent to fix this issue:</p>



<pre class="wp-block-code"><code>The users password field must be excluded from the responses.</code></pre>



<h2 class="wp-block-heading" id="h-last-task-add-jwt-authentication">Last task, add JWT authentication</h2>



<p class="wp-block-paragraph">As authentication mechanism, I use <a href="https://docs.nestjs.com/recipes/passport#jwt-functionality">JWT</a> tokens provided by <a href="https://www.passportjs.org/">passport</a> library.</p>



<p class="wp-block-paragraph">I install the required packages:</p>



<pre class="wp-block-code"><code>npm install --save @nestjs/passport
npm install --save @nestjs/jwt passport-jwt
npm install --save-dev @types/passport-jwt</code></pre>



<p class="wp-block-paragraph">Then, I ask the AI agent to generate the code:</p>



<pre class="wp-block-code"><code>Implement the JWT authentication strategy, @nestjs/jwt passport-jwt and @types/passport-jwt are installed.
Add a login endpoint that returns a JWT token when provided with valid user credentials (username and password from the users table).</code></pre>



<p class="wp-block-paragraph">And I instruct the AI to use .env file for the JWT secret and expiration:</p>



<pre class="wp-block-code"><code>Add the JWT secrets and expiration into the .env file, Fix the typescript errors, Improve the swagger documentation for login endpoint (message definition)</code></pre>



<p class="wp-block-paragraph">Now, I want to secure the users endpoints to allow only authenticated users and ask the agent the following:</p>



<pre class="wp-block-code"><code>Add a guard on the users endpoints to allow only connected users</code></pre>



<p class="wp-block-paragraph">Last point, I want to be able to authenticate on the Swagger interface, so I ask it:</p>



<pre class="wp-block-code"><code>Add the ability to authenticate on the Swagger interface with a bearer token.</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="570" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-Auth-1024x570.png" alt="" class="wp-image-41787" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-Auth-1024x570.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-Auth-300x167.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-Auth-768x428.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/12/Swagger-Auth.png 1506w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading" id="h-conclusion"><strong>Conclusion</strong></h2>



<p class="wp-block-paragraph">All of this took me around <strong>1h30</strong> to complete, including prompting and reviewing the steps.</p>



<p class="wp-block-paragraph">Reading the documentation, understanding the technologies, adding the dependancies remained the same.</p>



<p class="wp-block-paragraph">The initial estimate, without AI, was between 2 and 4 hours to complete the project :</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><th>Task</th><th>Estimated Time</th><th>AI Coding / prompting</th><th>Review</th></tr><tr><td>.env</td><td>15–30 min</td><td>6 min</td><td>5 min</td></tr><tr><td>Connexion PostgreSQL</td><td>20–40 min</td><td>4 min</td><td>2 min</td></tr><tr><td>Table User + migration</td><td>15–25 min</td><td>7 min</td><td>2 min</td></tr><tr><td>CRUD Users</td><td>30–45 min</td><td>5 min</td><td>10 min</td></tr><tr><td>Swagger UI</td><td>15–30 min</td><td>6 min</td><td>6 min</td></tr><tr><td>Auth JWT</td><td>30–60 min</td><td>12 min</td><td>15 min</td></tr><tr><td>Guards</td><td>15–30 min</td><td>5 min</td><td>5 min</td></tr><tr><td><strong>TOTAL</strong></td><td>2h20 – 4h20</td><td>45min</td><td>45 min</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">During development, AI makes certain errors or inaccuracies like TypeScripts compilation errors, password or security issues such as returning the password hash in the user list. However, the time spent to review and correct these issues is largely compensated by the speed of code generation.</p>



<p class="wp-block-paragraph">At the end, coding with AI is very fast, the generated code with a well documented technology (NestJS) is good.</p>



<p class="wp-block-paragraph">Even if formulating a clear request requires careful consideration and wording, coding is comfortable. However, the job is no longer the same; it now requires good code planning and architecture and the ability to review the generated code. Coding with AI can be effective, but only if you have a clear idea of what you want from the very beginning, use clear instructions and leave no room for interpretation by the AI.</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-effective-is-ai-on-a-development-project/">How effective is AI on a development project?</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/how-effective-is-ai-on-a-development-project/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Avoid CORS requests in development mode with Vite</title>
		<link>https://www.dbi-services.com/blog/avoid-cors-requests-in-development-mode-with-vite/</link>
					<comments>https://www.dbi-services.com/blog/avoid-cors-requests-in-development-mode-with-vite/#respond</comments>
		
		<dc:creator><![CDATA[Nicolas Meunier]]></dc:creator>
		<pubDate>Wed, 26 Nov 2025 16:49:31 +0000</pubDate>
				<category><![CDATA[Development & Performance]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[NestJS]]></category>
		<category><![CDATA[Vite]]></category>
		<category><![CDATA[Vue3]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=41628</guid>

					<description><![CDATA[<p>When developing a modern web application, it&#8217;s common to use a frontend (Vue, React) that communicates with a backend (Nest.js) via REST APIs. Often, the two projects live in parallel, requiring them to be run in development mode. Without specific configuration, API calls between the frontend and backend will be blocked. In my case, with [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/avoid-cors-requests-in-development-mode-with-vite/">Avoid CORS requests in development mode with Vite</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">When developing a modern web application, it&#8217;s common to use a frontend (Vue, React) that communicates with a backend (Nest.js) via REST APIs. Often, the two projects live in parallel, requiring them to be run in development mode. Without specific configuration, API calls between the frontend and backend will be blocked.</p>



<p class="wp-block-paragraph">In my case, with a frontend in <a href="https://vuejs.org/">Vue 3</a> and <a href="https://nestjs.com/">NestJS</a> as backend, as small <a href="https://vite.dev/">Vite</a> configuration helps to overcome the problem in local development.</p>



<h2 class="wp-block-heading" id="h-configuration-of-the-vue-3-frontend-vite"><strong>Configuration of the Vue 3 frontend (Vite)</strong></h2>



<p class="wp-block-paragraph">Important point, the two technologies used for my project are based on Vite. As a result, by default, both projects try to use port 3000 in development mode. First, I have to change the port used by Vite in the Vue project (the frontend) to a new one (port 4200 in my case).</p>



<pre class="wp-block-code"><code>// vite.config.js
export default defineConfig({
  // all the config...
  server: {
    port: 4200, // new custom port for development
  }
})</code></pre>



<h2 class="wp-block-heading" id="h-the-cors-request-problem"><strong>The CORS request problem</strong></h2>



<p class="wp-block-paragraph">Now the two projects can run at the same time without port conflicts. The NestJS backend run on the default port (localhost:3000) and the Vue frontend on the newly defined one (localhost:4200), but the problem isn&#8217;t solved yet. The API calls from the frontend to the backend generate errors like this :</p>



<pre class="wp-block-code"><code>Access to fetch at 'http://localhost:3000/status' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.</code></pre>



<p class="wp-block-paragraph">This error is due to security rules on the browser. Without cross-origin configuration on the server, it&#8217;s not possible to fetch url with different domain, protocol or port.</p>



<p class="wp-block-paragraph">One solution is to enable CORS on the backend server, but this solution requires to add some code at server side to manage a configuration that is not required in production. Indeed, it&#8217;s never a good idea to have a specific behavior only for development use cases.</p>



<h2 class="wp-block-heading" id="h-solution-with-vite"><strong>Solution with Vite</strong></h2>



<p class="wp-block-paragraph">The solution is very simple with the proxy feature in the Vite development server: Vite is capable of proxifying the API and avoid CORS requests to be sent to the backend. To do that, I&#8217;ve added another piece of configuration to the frontend vite.config.js file:</p>



<pre class="wp-block-code"><code>// vite.config.js
export default defineConfig({
  // others configurations...
  server: {
    port: 4200, // frontend port
    proxy: {
      '/api': {
        target: 'http://localhost:3000', // NestJS backend address
        changeOrigin: true,
      }
    }
  }
})</code></pre>



<p class="wp-block-paragraph">With this config all trafic to &#8220;http://localhost:4200/api/&#8230;&#8221; will be proxified to &#8220;http://localhost:3000/api/&#8230;&#8221;. </p>



<p class="wp-block-paragraph">As a result, all requests come from the same origin (localhost:4200) and no CORS errors are thrown by the browser.</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/avoid-cors-requests-in-development-mode-with-vite/">Avoid CORS requests in development mode with Vite</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/avoid-cors-requests-in-development-mode-with-vite/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to add Column Filtering to Vuetify Data Table</title>
		<link>https://www.dbi-services.com/blog/how-to-add-column-filtering-to-vuetify-data-table/</link>
					<comments>https://www.dbi-services.com/blog/how-to-add-column-filtering-to-vuetify-data-table/#respond</comments>
		
		<dc:creator><![CDATA[Joan Frey]]></dc:creator>
		<pubDate>Mon, 25 Aug 2025 19:22:27 +0000</pubDate>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[YaK]]></category>
		<category><![CDATA[vue]]></category>
		<category><![CDATA[vuejs]]></category>
		<category><![CDATA[vuetify]]></category>
		<category><![CDATA[yak]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=38050</guid>

					<description><![CDATA[<p>For a long time, my v-data-table-server implementation in the YaK only supported global search through a simple text input. While it was good enough for basic queries, I needed something more powerful—the ability to filter by individual columns using custom operators. Here’s a walkthrough of how I added column filters with v-select and v-text-field embedded [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-add-column-filtering-to-vuetify-data-table/">How to add Column Filtering to Vuetify Data Table</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">For a long time, my v-data-table-server implementation in the <a href="https://yak4all.io/">YaK </a>only supported global search through a simple text input. While it was good enough for basic queries, I needed something more powerful—the ability to filter by individual columns using custom operators.</p>



<p class="wp-block-paragraph">Here’s a walkthrough of how I added column filters with v-select and v-text-field embedded in v-menu components right inside the table headers.</p>



<h2 class="wp-block-heading" id="h-the-problem">The Problem</h2>



<p class="wp-block-paragraph">My users wanted to search not just by a global term but with more granularity—for example:</p>



<ul class="wp-block-list">
<li>Show only items where provider contains “aws”</li>



<li>Filter out items with a specific state</li>



<li>View all entries with a name that contains “master”</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="468" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-34-1024x468.png" alt="" class="wp-image-38053" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-34-1024x468.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-34-300x137.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-34-768x351.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-34-1536x702.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-34.png 1761w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">The default Vuetify Data Table doesn’t support per-column filtering out of the box, so I needed to get creative, even if you can override the default filtering used with the <strong><a href="https://vuetifyjs.com/en/components/data-tables/data-and-display/#custom-filter">search</a></strong> prop.</p>



<h2 class="wp-block-heading" id="h-the-approach">The Approach</h2>



<ul class="wp-block-list">
<li><code>Attach filter UI elements to each column header</code></li>



<li><code>Bind filters to a filters object keyed by column title</code></li>



<li><code>Apply the filters to the data using a computed property</code></li>



<li><code>Change the icon and icon color on the header to indicate active filters</code></li>
</ul>



<h2 class="wp-block-heading" id="h-i-injecting-the-filter-ui-into-headers">I. Injecting the Filter UI into Headers</h2>



<p class="wp-block-paragraph">In the v-slot:[header.key], I added a v-menu that displays a filter operator dropdown and a text input:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: xml; title: ; notranslate">
&lt;template
  v-for=&quot;header in dataHeaders&quot;
  v-slot:&#x5B;header.${header.key}]=&quot;{ column }&quot;
  :key=&quot;header.key&quot;
&gt;
  {{ column.title }}
  &lt;v-menu :close-on-content-click=&quot;false&quot;&gt;
    &lt;template v-slot:activator=&quot;{ props }&quot;&gt;
      &lt;v-btn icon v-bind=&quot;props&quot; color=&quot;transparent&quot;&gt;
        &lt;v-icon v-if=&quot;filters&#x5B;column.title].value&quot;&gt;mdi-filter&lt;/v-icon&gt;
        &lt;v-icon v-else&gt;mdi-filter-outline&lt;/v-icon&gt;
      &lt;/v-btn&gt;
    &lt;/template&gt;
    &lt;div class=&quot;filter-menu&quot;&gt;
      &lt;v-select
        v-model=&quot;filters&#x5B;column.title].operator&quot;
        :items=&quot;&#x5B;&#039;=&#039;, &#039;!=&#039;]&quot;
        label=&quot;Operator&quot;
        variant=&quot;outlined&quot;
        density=&quot;compact&quot;
      &gt;&lt;/v-select&gt;
      &lt;v-text-field
        v-model=&quot;filters&#x5B;column.title].value&quot;
        label=&quot;Search Term&quot;
        variant=&quot;outlined&quot;
        clearable
        density=&quot;compact&quot;
      &gt;&lt;/v-text-field&gt;
    &lt;/div&gt;
  &lt;/v-menu&gt;
&lt;/template&gt;
</pre></div>


<p class="wp-block-paragraph">Each filter’s state is stored in a reactive object:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
const filters = ref&lt;Record&lt;string, { value: string; operator: string }&gt;&gt;({});
</pre></div>


<h2 class="wp-block-heading" id="h-ii-initializing-filters-for-each-column">II. Initializing Filters for Each Column</h2>



<p class="wp-block-paragraph">Once I loaded the table headers, I made sure to initialize the corresponding filters:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
dataHeaders.value.forEach((header) =&gt; {
    if (!filters.value&#x5B;header.title]) {
        filters.value&#x5B;header.title] = { value: &quot;&quot;, operator: &quot;=&quot; };
     }
});
</pre></div>


<p class="wp-block-paragraph">This ensured every column could be filtered independently and to avoid errors related to empty/null data.</p>



<h2 class="wp-block-heading" id="h-iii-applying-the-filters-to-the-data">III. Applying the Filters to the Data</h2>



<p class="wp-block-paragraph">I used a computed property to transform the data on the fly:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
const filteredData = computed(() =&gt; {
  return recordData.value.filter((item) =&gt; {
    return Object.entries(filters.value).every((&#x5B;filterKey, filter]) =&gt; {
      if (!filter.value) return true;

      const matchingKey = Object.keys(item).find((key) =&gt; {
        const normalizedFilterKey = filterKey.toLowerCase();
        return key.toLowerCase() === normalizedFilterKey ||
               key.toLowerCase().endsWith(&quot;name&quot;) &amp;&amp;
               key.toLowerCase().startsWith(normalizedFilterKey);
      });

      if (!matchingKey) return true;

      const itemValue = String(item&#x5B;matchingKey] || &quot;&quot;).toLowerCase();
      const filterValue = filter.value.toLowerCase();

      return filter.operator === &quot;=&quot;
        ? itemValue.includes(filterValue)
        : !itemValue.includes(filterValue);
    });
  });
});
</pre></div>


<p class="wp-block-paragraph">This filtering happens client-side after the data is fetched.</p>



<h2 class="wp-block-heading" id="h-iv-adding-ux-details">IV. Adding UX Details</h2>



<p class="wp-block-paragraph">A few extra things I did for better UX:</p>



<ul class="wp-block-list">
<li>Colored icons: A regular filter icon shows when a filter is active.</li>



<li>Clear filters button (optional): Could be added for each column.</li>



<li>Auto-focus: Focuses on the text field when the menu opens.</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: xml; title: ; notranslate">
&lt;v-data-table-server
    v-model:items-per-page=&quot;itemsPerPage&quot;
    :items-per-page-options=&quot;itemsPerPageOptions&quot;
    :max-height=&quot;&#039;50vh&#039;&quot;
    fixed-header
    v-model=&quot;selected&quot;
    class=&quot;rounded-lg&quot;
    :headers=&quot;dataHeaders&quot;
    :items-length=&quot;recordLength&quot;
    :items=&quot;filteredData&quot;
    :loading=&quot;data.loading&quot;
    :search=&quot;search&quot;
    :select-strategy=&quot;&#039;all&#039;&quot;
    @update:options=&quot;loadRecord&quot;
    @click:row=&quot;onRowClick&quot;
    show-select
  &gt;
    &lt;template
      v-for=&quot;dataKey in dataHeaders&quot;
      v-slot:&#x5B;`item.${dataKey.key}`]=&quot;{ item }&quot;
    &gt;
      &lt;slot
        v-if=&quot;item&quot;
        :name=&quot;dataKey.key&quot;
        :value=&quot;item&#x5B;dataKey.key]&quot;
        :record=&quot;item&quot;
        &gt;{{ item&#x5B;dataKey.key] }}&lt;/slot
      &gt;
      &lt;span :key=&quot;`${dataKey.key}-placeholder`&quot; v-else&gt;-&lt;/span&gt;
    &lt;/template&gt;

    &lt;template
      v-for=&quot;header in dataHeaders&quot;
      v-slot:&#x5B;`header.${header.key}`]=&quot;{ column }&quot;
      :key=&quot;header.key&quot;
    &gt;
      {{ column.title }}
      &lt;v-menu :close-on-content-click=&quot;false&quot;&gt;
        &lt;template v-slot:activator=&quot;{ props }&quot;&gt;
          {{ filters&#x5B;header] }}
          &lt;v-btn
            icon
            v-bind=&quot;props&quot;
            color=&quot;rgba(255, 0, 0, 0.0)&quot;
            style=&quot;box-shadow: none&quot;
          &gt;
            &lt;v-icon v-if=&quot;filters&#x5B;column!.title!].value == &#039;&#039; ||filters&#x5B;column!.title!].value == null&quot; color=&quot;white&quot; size=&quot;small&quot;&gt;mdi-filter-outline&lt;/v-icon&gt;
            &lt;v-icon v-else color=&quot;orange&quot; size=&quot;small&quot;&gt;mdi-filter&lt;/v-icon&gt;
          &lt;/v-btn&gt;
        &lt;/template&gt;
        &lt;div style=&quot;background-color: white; width: 200px; border-radius: 10px;&quot;&gt;
          &lt;v-select
            v-model=&quot;filters&#x5B;column.title!].operator&quot;
            :items=&quot;&#x5B;&#039;=&#039;, &#039;!=&#039;]&quot;
            label=&quot;Select operator&quot;
            class=&quot;pt-4 pl-4 pr-4&quot;
            variant=&quot;outlined&quot;
            density=&quot;compact&quot;
          &gt;&lt;/v-select&gt;
          &lt;v-text-field
            v-model=&quot;filters&#x5B;column.title!].value&quot;
            class=&quot;pl-4 pr-4&quot;
            type=&quot;text&quot;
            label=&quot;Enter the search term&quot;
            :autofocus=&quot;true&quot;
            variant=&quot;outlined&quot;
            clearable
            density=&quot;compact&quot;
          &gt;&lt;/v-text-field&gt;
        &lt;/div&gt;
      &lt;/v-menu&gt;
    &lt;/template&gt;
  &lt;/v-data-table-server&gt;
</pre></div>


<h2 class="wp-block-heading" id="h-what-s-next">What’s Next?</h2>



<p class="wp-block-paragraph">After adding this new feature, the sort indicator next to each column header disappeared—although sorting still works as expected. I haven’t had time to look into it yet, but I’d like to find a solution. If you happen to figure it out, feel free to share it in the comments!</p>



<h2 class="wp-block-heading" id="h-where-to-find-the-complete-code">Where to find the complete code ?</h2>



<p class="wp-block-paragraph">You can check out the complete and latest code in the open-source project on GitLab:<br>👉 <a class="" href="https://gitlab.com/yak4all/yak_frontend/yak_ui">https://gitlab.com/yak4all/yak_frontend/yak_ui</a><br>The relevant logic lives in the <code>YakGrid.vue</code> component.</p>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p class="wp-block-paragraph">By embedding filter controls directly into column headers and managing state reactively, I was able to turn a basic Vuetify data table into a much more powerful data exploration tool.</p>



<p class="wp-block-paragraph">Let me know if you try this pattern or come up with improvements—I’m always up for iterating! You can also suggest changes or contribute directly on the GitLab repo of the Yak project.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-add-column-filtering-to-vuetify-data-table/">How to add Column Filtering to Vuetify Data Table</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/how-to-add-column-filtering-to-vuetify-data-table/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Getting started with Open WebUI</title>
		<link>https://www.dbi-services.com/blog/getting-started-with-open-webui/</link>
					<comments>https://www.dbi-services.com/blog/getting-started-with-open-webui/#comments</comments>
		
		<dc:creator><![CDATA[Daniel Westermann]]></dc:creator>
		<pubDate>Fri, 11 Apr 2025 06:25:24 +0000</pubDate>
				<category><![CDATA[Enterprise content management]]></category>
		<category><![CDATA[Web]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=37988</guid>

					<description><![CDATA[<p>Everybody is talking about AI, RAG and language models today. A lot of people are using ChatGPT or other tools to get answers to their questions, generate text or images. While this sometimes is really helpful (depending on how you use it) you still need to be skeptical about the information you get out of [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/getting-started-with-open-webui/">Getting started with Open WebUI</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Everybody is talking about <a href="https://en.wikipedia.org/wiki/Artificial_intelligence" target="_blank" rel="noreferrer noopener">AI</a>, <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation" target="_blank" rel="noreferrer noopener">RAG</a> and <a href="https://en.wikipedia.org/wiki/Language_model" target="_blank" rel="noreferrer noopener">language models</a> today. A lot of people are using <a href="https://openai.com/index/chatgpt/" target="_blank" rel="noreferrer noopener">ChatGPT</a> or other tools to get answers to their questions, generate text or images. While this sometimes is really helpful (depending on how you use it) you still need to be skeptical about the information you get out of those tools. To understand how those tools work, you need to touch and work with them. In this post we&#8217;ll take a look at  <a href="https://github.com/open-webui/open-webui" target="_blank" rel="noreferrer noopener">Open WebUI</a>, which is an open source &#8220;user-friendly self-hosted AI platform designed to operate entirely offline&#8221;.</p>



<p class="wp-block-paragraph">Before we can setup Open WebUI we need a large language model, and one of the popular frameworks is <a href="https://ollama.com/" target="_blank" rel="noreferrer noopener">Ollama</a>. Getting this up and running as quite easy and we&#8217;ll start from a Debian 12 minimal installation:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1]; title: ; notranslate">
root@ip-10-0-1-112:~$ cat /etc/debian_version 
12.10
</pre></div>


<p class="wp-block-paragraph">The installation is actually a one-liner, and all you have to do is this:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; highlight: [1]; title: ; notranslate">
root@ip-10-0-1-112:~$ curl -fsSL https://ollama.com/install.sh | sh
&gt;&gt;&gt; Installing ollama to /usr/local
&gt;&gt;&gt; Downloading Linux amd64 bundle
######################################################################## 100.0%
&gt;&gt;&gt; Creating ollama user...
&gt;&gt;&gt; Adding ollama user to render group...
&gt;&gt;&gt; Adding ollama user to video group...
&gt;&gt;&gt; Adding current user to ollama group...
&gt;&gt;&gt; Creating ollama systemd service...
&gt;&gt;&gt; Enabling and starting ollama service...
Created symlink /etc/systemd/system/default.target.wants/ollama.service → /etc/systemd/system/ollama.service.
&gt;&gt;&gt; The Ollama API is now available at 127.0.0.1:11434.
&gt;&gt;&gt; Install complete. Run &quot;ollama&quot; from the command line.
WARNING: No NVIDIA/AMD GPU detected. Ollama will run in CPU-only mode.
</pre></div>


<p class="wp-block-paragraph">This created a new operating system user and started a new process:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; highlight: [1,3,6]; title: ; notranslate">
root@ip-10-0-1-112:~$ grep ollama /etc/passwd
ollama:x:999:994::/usr/share/ollama:/bin/false
root@ip-10-0-1-112:~$ ps -ef | grep ollama
ollama      1194       1  0 05:01 ?        00:00:00 /usr/local/bin/ollama serve
root        1205     606  0 05:01 pts/1    00:00:00 grep ollama
root@ip-10-0-1-112:~$ systemctl list-unit-files | grep ollama
ollama.service                         enabled         enabled
</pre></div>


<p class="wp-block-paragraph">From now on you already can run the model and start asking questions:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; highlight: [1,2]; title: ; notranslate">
root@ip-10-0-1-112:~$ ollama run llama3.2
&gt;&gt;&gt; Who am I?
I&#039;d love to try and figure out who you are, but I need a bit more information. You see, there are many people with different names, backgrounds, and characteristics, so it&#039;s not always possible to pinpoint exactly who someone is.

Can you give me some clues or hints about yourself? For example:

* What do you like doing in your free time?
* Do you have any unique talents or skills?
* Are you a fan of any particular sports teams, movies, or TV shows?
* Do you have any fun facts or accomplishments that might help me get to know you better?

The more information you share, the better I&#039;ll be able to try and guess who you are!

&gt;&gt;&gt; Send a message (/? for help)
</pre></div>


<p class="wp-block-paragraph">As we want a graphical user interface over this, we can now go ahead an install Open WebUI. This is quite easy as well. We&#8217;ll use a Python virtual environment for this:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; highlight: [1,2,3,4,5,6]; title: ; notranslate">
root@ip-10-0-1-112:~$ apt install -y pip python3-venv
root@ip-10-0-1-112:~$ groupadd openwebui
root@ip-10-0-1-112:~$ useradd -g openwebui -m -s /bin/bash openwebui
root@ip-10-0-1-112:~$ su - openwebui
openwebui@ip-10-0-1-112:~$ python3 -m venv .local
openwebui@ip-10-0-1-112:~$ .local/bin/pip install open-webui
Collecting open-webui
  Downloading open_webui-0.6.2-py3-none-any.whl (132.7 MB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 132.7/132.7 MB 18.7 MB/s eta 0:00:00
Collecting accelerate
  Downloading accelerate-1.6.0-py3-none-any.whl (354 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 354.7/354.7 kB 27.4 MB/s eta 0:00:00
Collecting aiocache
  Downloading aiocache-0.12.3-py2.py3-none-any.whl (28 kB)
Collecting aiofiles
  Downloading aiofiles-24.1.0-py3-none-any.whl (15 kB)
Collecting aiohttp==3.11.11
...
Successfully installed Events-0.5 Jinja2-3.1.6 Mako-1.3.10 MarkupSafe-3.0.2 PyYAML-6.0.2 RTFDE-0.1.2 Shapely-2.1.0 XlsxWriter-3.2.2 accelerate-1.6.0 aiocache-0.12.3 aiofiles-24.1.0 aiohappyeyeballs-2.6.1 aiohttp-3.11.11 aiosignal-1.3.2 alembic-1.14.0 annotated-types-0.7.0 anthropic-0.49.0 anyio-4.9.0 appdirs-1.4.4 apscheduler-3.10.4 argon2-cffi-23.1.0 argon2-cffi-bindings-21.2.0 asgiref-3.8.1 async-timeout-5.0.1 attrs-25.3.0 authlib-1.4.1 av-14.3.0 azure-ai-documentintelligence-1.0.0 azure-core-1.33.0 azure-identity-1.20.0 azure-storage-blob-12.24.1 backoff-2.2.1 bcrypt-4.3.0 beautifulsoup4-4.13.3 bidict-0.23.1 bitarray-3.3.1 black-25.1.0 blinker-1.9.0 boto3-1.35.53 botocore-1.35.99 build-1.2.2.post1 cachetools-5.5.2 certifi-2025.1.31 cffi-1.17.1 chardet-5.2.0 charset-normalizer-3.4.1 chroma-hnswlib-0.7.6 chromadb-0.6.2 click-8.1.8 colbert-ai-0.2.21 colorclass-2.2.2 coloredlogs-15.0.1 compressed-rtf-1.0.7 cryptography-44.0.2 ctranslate2-4.6.0 dataclasses-json-0.6.7 datasets-3.5.0 defusedxml-0.7.1 deprecated-1.2.18 dill-0.3.8 distro-1.9.0 dnspython-2.7.0 docker-7.1.0 docx2txt-0.8 duckduckgo-search-7.3.2 durationpy-0.9 easygui-0.98.3 ebcdic-1.1.1 ecdsa-0.19.1 einops-0.8.1 elastic-transport-8.17.1 elasticsearch-8.17.1 emoji-2.14.1 et-xmlfile-2.0.0 eval-type-backport-0.2.2 extract-msg-0.54.1 fake-useragent-2.1.0 fastapi-0.115.7 faster-whisper-1.1.1 filelock-3.18.0 filetype-1.2.0 firecrawl-py-1.12.0 flask-3.1.0 flatbuffers-25.2.10 fonttools-4.57.0 fpdf2-2.8.2 frozenlist-1.5.0 fs-2.4.16 fsspec-2024.12.0 ftfy-6.2.3 gcp-storage-emulator-2024.8.3 git-python-1.0.3 gitdb-4.0.12 gitpython-3.1.44 google-ai-generativelanguage-0.6.15 google-api-core-2.24.2 google-api-python-client-2.166.0 google-auth-2.38.0 google-auth-httplib2-0.2.0 google-auth-oauthlib-1.2.1 google-cloud-core-2.4.3 google-cloud-storage-2.19.0 google-crc32c-1.7.1 google-generativeai-0.8.4 google-resumable-media-2.7.2 googleapis-common-protos-1.63.2 greenlet-3.1.1 grpcio-1.67.1 grpcio-status-1.67.1 grpcio-tools-1.67.1 h11-0.14.0 h2-4.2.0 hpack-4.1.0 html5lib-1.1 httpcore-1.0.7 httplib2-0.22.0 httptools-0.6.4 httpx-0.28.1 httpx-sse-0.4.0 huggingface-hub-0.30.2 humanfriendly-10.0 hyperframe-6.1.0 idna-3.10 importlib-metadata-8.6.1 importlib-resources-6.5.2 iniconfig-2.1.0 isodate-0.7.2 itsdangerous-2.2.0 jiter-0.9.0 jmespath-1.0.1 joblib-1.4.2 jsonpatch-1.33 jsonpointer-3.0.0 kubernetes-32.0.1 langchain-0.3.19 langchain-community-0.3.18 langchain-core-0.3.51 langchain-text-splitters-0.3.8 langdetect-1.0.9 langfuse-2.44.0 langsmith-0.3.30 lark-1.1.9 ldap3-2.9.1 loguru-0.7.2 lxml-5.3.2 markdown-3.7 markdown-it-py-3.0.0 marshmallow-3.26.1 mdurl-0.1.2 milvus-lite-2.4.12 mmh3-5.1.0 monotonic-1.6 moto-5.1.3 mpmath-1.3.0 msal-1.32.0 msal-extensions-1.3.1 msoffcrypto-tool-5.4.2 multidict-6.4.3 multiprocess-0.70.16 mypy-extensions-1.0.0 nest-asyncio-1.6.0 networkx-3.4.2 ninja-1.11.1.4 nltk-3.9.1 numpy-1.26.4 nvidia-cublas-cu12-12.4.5.8 nvidia-cuda-cupti-cu12-12.4.127 nvidia-cuda-nvrtc-cu12-12.4.127 nvidia-cuda-runtime-cu12-12.4.127 nvidia-cudnn-cu12-9.1.0.70 nvidia-cufft-cu12-11.2.1.3 nvidia-curand-cu12-10.3.5.147 nvidia-cusolver-cu12-11.6.1.9 nvidia-cusparse-cu12-12.3.1.170 nvidia-cusparselt-cu12-0.6.2 nvidia-nccl-cu12-2.21.5 nvidia-nvjitlink-cu12-12.4.127 nvidia-nvtx-cu12-12.4.127 oauthlib-3.2.2 olefile-0.47 oletools-0.60.2 onnxruntime-1.20.1 open-webui-0.6.2 openai-1.72.0 opencv-python-4.11.0.86 opencv-python-headless-4.11.0.86 openpyxl-3.1.5 opensearch-py-2.8.0 opentelemetry-api-1.32.0 opentelemetry-exporter-otlp-proto-common-1.32.0 opentelemetry-exporter-otlp-proto-grpc-1.32.0 opentelemetry-instrumentation-0.53b0 opentelemetry-instrumentation-asgi-0.53b0 opentelemetry-instrumentation-fastapi-0.53b0 opentelemetry-proto-1.32.0 opentelemetry-sdk-1.32.0 opentelemetry-semantic-conventions-0.53b0 opentelemetry-util-http-0.53b0 orjson-3.10.16 overrides-7.7.0 packaging-23.2 pandas-2.2.3 passlib-1.7.4 pathspec-0.12.1 pcodedmp-1.2.6 peewee-3.17.9 peewee-migrate-1.12.2 pgvector-0.3.5 pillow-11.1.0 platformdirs-4.3.7 playwright-1.49.1 pluggy-1.5.0 portalocker-2.10.1 posthog-3.24.0 primp-0.14.0 propcache-0.3.1 proto-plus-1.26.1 protobuf-5.29.4 psutil-7.0.0 psycopg2-binary-2.9.9 py-partiql-parser-0.6.1 pyarrow-19.0.1 pyasn1-0.4.8 pyasn1-modules-0.4.1 pyclipper-1.3.0.post6 pycparser-2.22 pydantic-2.10.6 pydantic-core-2.27.2 pydantic-settings-2.8.1 pydub-0.25.1 pyee-12.0.0 pygments-2.19.1 pyjwt-2.10.1 pymdown-extensions-10.14.2 pymilvus-2.5.0 pymongo-4.12.0 pymysql-1.1.1 pypandoc-1.15 pyparsing-3.2.3 pypdf-4.3.1 pypika-0.48.9 pyproject_hooks-1.2.0 pytest-8.3.5 pytest-docker-3.1.2 python-dateutil-2.9.0.post0 python-dotenv-1.1.0 python-engineio-4.11.2 python-iso639-2025.2.18 python-jose-3.4.0 python-magic-0.4.27 python-multipart-0.0.18 python-oxmsg-0.0.2 python-pptx-1.0.0 python-socketio-5.11.3 pytube-15.0.0 pytz-2025.2 pyxlsb-1.0.10 qdrant-client-1.12.2 rank-bm25-0.2.2 rapidfuzz-3.13.0 rapidocr-onnxruntime-1.3.24 red-black-tree-mod-1.22 redis-5.2.1 regex-2024.11.6 requests-2.32.3 requests-oauthlib-2.0.0 requests-toolbelt-1.0.0 responses-0.25.7 restrictedpython-8.0 rich-14.0.0 rsa-4.9 s3transfer-0.10.4 safetensors-0.5.3 scikit-learn-1.6.1 scipy-1.15.2 sentence-transformers-3.3.1 sentencepiece-0.2.0 setuptools-78.1.0 shellingham-1.5.4 simple-websocket-1.1.0 six-1.17.0 smmap-5.0.2 sniffio-1.3.1 soundfile-0.13.1 soupsieve-2.6 sqlalchemy-2.0.38 starlette-0.45.3 sympy-1.13.1 tenacity-9.1.2 threadpoolctl-3.6.0 tiktoken-0.9.0 tokenizers-0.21.1 torch-2.6.0 tqdm-4.67.1 transformers-4.51.2 triton-3.2.0 typer-0.15.2 typing-extensions-4.13.2 typing-inspect-0.9.0 typing-inspection-0.4.0 tzdata-2025.2 tzlocal-5.3.1 ujson-5.10.0 unstructured-0.16.17 unstructured-client-0.32.3 uritemplate-4.1.1 urllib3-2.4.0 uvicorn-0.34.0 uvloop-0.21.0 validators-0.34.0 watchfiles-1.0.5 wcwidth-0.2.13 webencodings-0.5.1 websocket-client-1.8.0 websockets-15.0.1 werkzeug-3.1.3 wrapt-1.17.2 wsproto-1.2.0 xlrd-2.0.1 xmltodict-0.14.2 xxhash-3.5.0 yarl-1.19.0 youtube-transcript-api-0.6.3 zipp-3.21.0 zstandard-0.23.0
openwebui@ip-10-0-1-112:~$

</pre></div>


<p class="wp-block-paragraph">Running that for the first time, will download additional requirements:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; highlight: [1]; title: ; notranslate">
openwebui@ip-10-0-1-112:~$ .local/bin/open-webui serve
Loading WEBUI_SECRET_KEY from file, not provided as an environment variable.
Generating a new secret key and saving it to /home/openwebui/.webui_secret_key
Loading WEBUI_SECRET_KEY from /home/openwebui/.webui_secret_key
/home/openwebui/.local/lib/python3.11/site-packages/open_webui
/home/openwebui/.local/lib/python3.11/site-packages
/home/openwebui/.local/lib/python3.11
INFO  &#x5B;alembic.runtime.migration] Context impl SQLiteImpl.
INFO  &#x5B;alembic.runtime.migration] Will assume non-transactional DDL.
INFO  &#x5B;alembic.runtime.migration] Running upgrade  -&gt; 7e5b5dc7342b, init
INFO  &#x5B;alembic.runtime.migration] Running upgrade 7e5b5dc7342b -&gt; ca81bd47c050, Add config table
INFO  &#x5B;alembic.runtime.migration] Running upgrade ca81bd47c050 -&gt; c0fbf31ca0db, Update file table
INFO  &#x5B;alembic.runtime.migration] Running upgrade c0fbf31ca0db -&gt; 6a39f3d8e55c, Add knowledge table
Creating knowledge table
Migrating data from document table to knowledge table
INFO  &#x5B;alembic.runtime.migration] Running upgrade 6a39f3d8e55c -&gt; 242a2047eae0, Update chat table
Converting &#039;chat&#039; column to JSON
Renaming &#039;chat&#039; column to &#039;old_chat&#039;
Adding new &#039;chat&#039; column of type JSON
Dropping &#039;old_chat&#039; column
INFO  &#x5B;alembic.runtime.migration] Running upgrade 242a2047eae0 -&gt; 1af9b942657b, Migrate tags
INFO  &#x5B;alembic.runtime.migration] Running upgrade 1af9b942657b -&gt; 3ab32c4b8f59, Update tags
Primary Key: {&#039;name&#039;: None, &#039;constrained_columns&#039;: &#x5B;]}
Unique Constraints: &#x5B;{&#039;name&#039;: &#039;uq_id_user_id&#039;, &#039;column_names&#039;: &#x5B;&#039;id&#039;, &#039;user_id&#039;]}]
Indexes: &#x5B;{&#039;name&#039;: &#039;tag_id&#039;, &#039;column_names&#039;: &#x5B;&#039;id&#039;], &#039;unique&#039;: 1, &#039;dialect_options&#039;: {}}]
Creating new primary key with &#039;id&#039; and &#039;user_id&#039;.
Dropping unique constraint: uq_id_user_id
Dropping unique index: tag_id
INFO  &#x5B;alembic.runtime.migration] Running upgrade 3ab32c4b8f59 -&gt; c69f45358db4, Add folder table
INFO  &#x5B;alembic.runtime.migration] Running upgrade c69f45358db4 -&gt; c29facfe716b, Update file table path
INFO  &#x5B;alembic.runtime.migration] Running upgrade c29facfe716b -&gt; af906e964978, Add feedback table
INFO  &#x5B;alembic.runtime.migration] Running upgrade af906e964978 -&gt; 4ace53fd72c8, Update folder table and change DateTime to BigInteger for timestamp fields
INFO  &#x5B;alembic.runtime.migration] Running upgrade 4ace53fd72c8 -&gt; 922e7a387820, Add group table
INFO  &#x5B;alembic.runtime.migration] Running upgrade 922e7a387820 -&gt; 57c599a3cb57, Add channel table
INFO  &#x5B;alembic.runtime.migration] Running upgrade 57c599a3cb57 -&gt; 7826ab40b532, Update file table
INFO  &#x5B;alembic.runtime.migration] Running upgrade 7826ab40b532 -&gt; 3781e22d8b01, Update message &amp; channel tables
INFO  &#x5B;open_webui.env] &#039;DEFAULT_LOCALE&#039; loaded from the latest database entry
INFO  &#x5B;open_webui.env] &#039;DEFAULT_PROMPT_SUGGESTIONS&#039; loaded from the latest database entry
WARNI &#x5B;open_webui.env]

WARNING: CORS_ALLOW_ORIGIN IS SET TO &#039;*&#039; - NOT RECOMMENDED FOR PRODUCTION DEPLOYMENTS.

INFO  &#x5B;open_webui.env] Embedding model set: sentence-transformers/all-MiniLM-L6-v2
/home/openwebui/.local/lib/python3.11/site-packages/pydub/utils.py:170: RuntimeWarning: Couldn&#039;t find ffmpeg or avconv - defaulting to ffmpeg, but may not work
  warn(&quot;Couldn&#039;t find ffmpeg or avconv - defaulting to ffmpeg, but may not work&quot;, RuntimeWarning)
WARNI &#x5B;langchain_community.utils.user_agent] USER_AGENT environment variable not set, consider setting it to identify your requests.

 ██████╗ ██████╗ ███████╗███╗   ██╗    ██╗    ██╗███████╗██████╗ ██╗   ██╗██╗
██╔═══██╗██╔══██╗██╔════╝████╗  ██║    ██║    ██║██╔════╝██╔══██╗██║   ██║██║
██║   ██║██████╔╝█████╗  ██╔██╗ ██║    ██║ █╗ ██║█████╗  ██████╔╝██║   ██║██║
██║   ██║██╔═══╝ ██╔══╝  ██║╚██╗██║    ██║███╗██║██╔══╝  ██╔══██╗██║   ██║██║
╚██████╔╝██║     ███████╗██║ ╚████║    ╚███╔███╔╝███████╗██████╔╝╚██████╔╝██║
 ╚═════╝ ╚═╝     ╚══════╝╚═╝  ╚═══╝     ╚══╝╚══╝ ╚══════╝╚═════╝  ╚═════╝ ╚═╝


v0.6.2 - building the best open-source AI user interface.

https://github.com/open-webui/open-webui

Fetching 30 files:   0%|                                                                                                                                                                                             | 0/30 &#x5B;00:00&lt;?, ?it/s]Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
config.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 612/612 &#x5B;00:00&lt;00:00, 3.22MB/s]
.gitattributes: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1.23k/1.23k &#x5B;00:00&lt;00:00, 6.85MB/s]
config.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 190/190 &#x5B;00:00&lt;00:00, 1.22MB/s]
README.md: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 10.5k/10.5k &#x5B;00:00&lt;00:00, 2.52MB/s]
modules.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 349/349 &#x5B;00:00&lt;00:00, 1.47MB/s]
modules.json:   0%|                                                                                                                                                                                               | 0.00/349 &#x5B;00:00&lt;?, ?B/sXet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet` ?B/s]
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
data_config.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 39.3k/39.3k &#x5B;00:00&lt;00:00, 6.93MB/s]
model.safetensors:  23%|████████████████████████████████████████▌                                                                                                                                       | 21.0M/90.9M &#x5B;00:00&lt;00:00, 185MB/sXet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`5MB/s]
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`                                                                                                                                                                               | 0.00/90.4M &#x5B;00:00&lt;?, ?B/s]
config_sentence_transformers.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 116/116 &#x5B;00:00&lt;00:00, 424kB/s]
model_qint8_arm64.onnx: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 23.0M/23.0M &#x5B;00:00&lt;00:00, 37.6MB/s]
model.onnx:  12%|█████████████████████                                                                                                                                                                 | 10.5M/90.4M &#x5B;00:00&lt;00:02, 38.4MB/sXet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`0MB/s]
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`██████████████████████████████████████████████████████████████████████                                                                                                | 41.9M/90.4M &#x5B;00:00&lt;00:01, 46.9MB/s]
model.safetensors: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 90.9M/90.9M &#x5B;00:01&lt;00:00, 75.8MB/s]
model_O4.onnx: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 45.2M/45.2M &#x5B;00:00&lt;00:00, 48.6MB/s]
model_O4.onnx:  93%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████             | 41.9M/45.2M &#x5B;00:00&lt;00:00, 50.8MB/sXet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`0MB/s]
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`
model_O2.onnx:  46%|███████████████████████████████████████████████████████████████████████████████████                                                                                                | 41.9M/90.3M &#x5B;00:01&lt;00:01, 40.4MB/sXet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet` ?B/s]
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`███████████████████████████████████████████████████████████████████████████████████████████████████████████████▋                                                      | 62.9M/90.4M &#x5B;00:01&lt;00:00, 64.3MB/s]
openvino_model.xml: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 211k/211k &#x5B;00:00&lt;00:00, 1.83MB/s]
model_quint8_avx2.onnx: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 23.0M/23.0M &#x5B;00:00&lt;00:00, 49.4MB/s]
model_O1.onnx: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 90.4M/90.4M &#x5B;00:01&lt;00:00, 55.7MB/s]
model.onnx:  81%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▊                                  | 73.4M/90.4M &#x5B;00:01&lt;00:00, 56.7MB/sXet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`1MB/s]
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍                                 | 73.4M/90.3M &#x5B;00:01&lt;00:00, 52.3MB/s]
Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`MB/s]
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`l:   0%|                                                                                                                                                                        | 0.00/368k &#x5B;00:00&lt;?, ?B/s]
openvino_model_qint8_quantized.xml: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 368k/368k &#x5B;00:00&lt;00:00, 2.21MB/s]
openvino_model_qint8_quantized.bin: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 22.9M/22.9M &#x5B;00:00&lt;00:00, 48.6MB/s]
model.onnx: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 90.4M/90.4M &#x5B;00:01&lt;00:00, 48.5MB/s]
model_O3.onnx: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 90.3M/90.3M &#x5B;00:01&lt;00:00, 48.5MB/s]
sentence_bert_config.json: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 53.0/53.0 &#x5B;00:00&lt;00:00, 198kB/s]
Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`?B/s]
WARNI &#x5B;huggingface_hub.file_download] Xet Storage is enabled for this repo, but the &#039;hf_xet&#039; package is not installed. Falling back to regular HTTP download. For better performance, install the package with: `pip install huggingface_hub&#x5B;hf_xet]` or `pip install hf_xet`                                                                                                                                                                                | 0.00/53.0 &#x5B;00:00&lt;?, ?B/s]
model_O2.onnx: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 90.3M/90.3M &#x5B;00:01&lt;00:00, 45.9MB/s]
special_tokens_map.json: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 112/112 &#x5B;00:00&lt;00:00, 8.29kB/s]
tokenizer_config.json: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 350/350 &#x5B;00:00&lt;00:00, 1.05MB/s]
train_script.py: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 13.2k/13.2k &#x5B;00:00&lt;00:00, 40.2MB/s]
tokenizer.json: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 466k/466k &#x5B;00:00&lt;00:00, 2.58MB/s]
vocab.txt: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 232k/232k &#x5B;00:00&lt;00:00, 2.55MB/s]
openvino_model.bin: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 90.3M/90.3M &#x5B;00:01&lt;00:00, 64.0MB/s]
pytorch_model.bin: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 90.9M/90.9M &#x5B;00:00&lt;00:00, 101MB/s]
rust_model.ot: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 90.9M/90.9M &#x5B;00:00&lt;00:00, 91.4MB/s]
tf_model.h5: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 91.0M/91.0M &#x5B;00:00&lt;00:00, 119MB/s]
Fetching 30 files: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 30/30 &#x5B;00:03&lt;00:00,  8.99it/s]
INFO:     Started server process &#x5B;3254]███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████▍                                | 73.4M/90.3M &#x5B;00:01&lt;00:00, 65.5MB/s]
INFO:     Waiting for application startup.██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 91.0M/91.0M &#x5B;00:00&lt;00:00, 141MB/s]
2025-04-11 05:19:56.035 | INFO     | open_webui.utils.logger:start_logger:140 - GLOBAL_LOG_LEVEL: INFO - {}
</pre></div>


<p class="wp-block-paragraph">Once this is done, the web interface is available at http://hostname:8080 and you&#8217;ll be asked to create an admin user:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="645" height="448" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-1.png" alt="" class="wp-image-37994" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-1.png 645w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-1-300x208.png 300w" sizes="auto, (max-width: 645px) 100vw, 645px" /></figure>



<p class="wp-block-paragraph">Afterwards we can ask the same question as before:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="382" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-2-1024x382.png" alt="" class="wp-image-37995" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-2-1024x382.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-2-300x112.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-2-768x287.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-2-1536x573.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-2.png 1586w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Adding other models, is just a matter of installing them:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
openwebui@ip-10-0-1-112:~$ ollama run deepseek-r1
pulling manifest 
pulling 96c415656d37... 100% ▕████████████████████████████████████████████████████▏ 4.7 GB                         
pulling 369ca498f347... 100% ▕████████████████████████████████████████████████████▏  387 B                         
pulling 6e4c38e1172f... 100% ▕████████████████████████████████████████████████████▏ 1.1 KB                         
pulling f4d24e9138dd... 100% ▕████████████████████████████████████████████████████▏  148 B                         
pulling 40fb844194b2... 100% ▕████████████████████████████████████████████████████▏  487 B                         
verifying sha256 digest 
writing manifest 
success 
&gt;&gt;&gt; Who am I?
&lt;think&gt;

&lt;/think&gt;

Hi! I&#039;m DeepSeek-R1, an artificial intelligence assistant created by DeepSeek. For comprehensive details about 
our models and products, we invite you to consult our official documentation.

&gt;&gt;&gt; Who am I?
&lt;think&gt;
Alright, the user just asked &quot;Who am I?&quot; again. They&#039;ve asked this before, so maybe they&#039;re seeking more 
information or clarification.

Looking at their history, they initially wanted to know who I am as an AI. Now, they&#039;re repeating it, perhaps 
wanting a deeper understanding of themselves or their identity in interactions with me.

I should respond in a way that&#039;s both genuine and helpful without overcomplicating things. Maybe I can reflect 
on what &quot;I&quot; means for different people and offer some insights based on common perspectives.

Including examples could make the response more relatable, like mentioning how my AI nature sets me apart from 
other chatbots or how humans use similar tools to explore their own identity.

I should keep it friendly and open-ended, inviting them to ask further questions if they&#039;re interested in a 
specific aspect.
&lt;/think&gt;

The term &quot;Who am I?&quot; is a philosophical question that many people have explored throughout history. For you, 
as an AI created by DeepSeek, the answer might be more about your purpose or nature rather than personal 
identity.

If you&#039;re asking about your identity or what sets you apart from other AI tools, I can say this: I&#039;m an 
advanced language model designed to assist with a wide range of tasks, including answering questions, 
providing information, and helping with creative writing. My purpose is to help users like you by giving 
accurate and helpful responses.

If you&#039;re asking about your personal identity or experiences as an AI, that&#039;s something only you can answer! 
Let me know if you&#039;d like to explore this topic further in a more specific way. 😊

&gt;&gt;&gt; Send a message (/? for help)
</pre></div>


<p class="wp-block-paragraph">This is automatically detected by Open WebUI and can be used right away:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="382" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-3-1024x382.png" alt="" class="wp-image-37996" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-3-1024x382.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-3-300x112.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-3-768x287.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-3-1536x573.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-3.png 1586w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="382" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-4-1024x382.png" alt="" class="wp-image-37997" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-4-1024x382.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-4-300x112.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-4-768x287.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-4-1536x573.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/openwebui-4.png 1586w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">That should be enough to get you started.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/getting-started-with-open-webui/">Getting started with Open WebUI</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/getting-started-with-open-webui/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Exploring FlyonUI with Vue.js: A Fresh and Evolving Library</title>
		<link>https://www.dbi-services.com/blog/exploring-flyonui-with-vue-js-a-fresh-and-evolving-library/</link>
					<comments>https://www.dbi-services.com/blog/exploring-flyonui-with-vue-js-a-fresh-and-evolving-library/#respond</comments>
		
		<dc:creator><![CDATA[Joan Frey]]></dc:creator>
		<pubDate>Thu, 31 Oct 2024 16:13:43 +0000</pubDate>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[FlyonUI]]></category>
		<category><![CDATA[tailwind]]></category>
		<category><![CDATA[vue]]></category>
		<category><![CDATA[vuejs]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=35551</guid>

					<description><![CDATA[<p>As someone who has relied on Vuetify for most of my Vue.js projects, I&#8217;m always on the lookout for new libraries that can streamline development and keep designs modern. Recently, a new UI library called FlyonUI was released, and I decided to give it a spin. FlyonUI is a Tailwind CSS Components Library. To my [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/exploring-flyonui-with-vue-js-a-fresh-and-evolving-library/">Exploring FlyonUI with Vue.js: A Fresh and Evolving Library</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">As someone who has relied on <strong>Vuetify</strong> for most of my Vue.js projects, I&#8217;m always on the lookout for new libraries that can streamline development and keep designs modern. Recently, a new UI library called <strong>FlyonUI</strong> was released, and I decided to give it a spin. FlyonUI is a Tailwind CSS Components Library. To my surprise, it turned out to be not only lightweight but also incredibly easy to set up and customize. In this post, I’ll walk you through the basics of integrating FlyonUI with Vue.js.</p>



<h2 class="wp-block-heading" id="h-why-i-m-interested-about-flyonui">Why I’m Interested About FlyonUI</h2>



<p class="wp-block-paragraph">Released just a few weeks ago, <strong>FlyonUI</strong> promises a fresh approach to Vue UI development. FlyonUI is an <strong>open-source</strong> (I love open-source projects) Tailwind CSS Components Library with semantic classes and powerful JS plugins. It’s designed to be intuitive, flexible and to have universal framework compatibility (Vuejs, React, Angular, &#8230;). I like their website and the documentation seems very complete, not even mentioning the fact that there is already more than 78 available components and 800 examples.</p>



<h2 class="wp-block-heading" id="h-prerequisites">Prerequisites</h2>



<ul class="wp-block-list">
<li>A basic understanding of Vue.js</li>



<li><strong>Node.js</strong> and <strong>npm</strong> installed</li>



<li>Vue.js running application set up with Tailwind CSS (<a href="https://tailwindcss.com/docs/guides/vite#vue">https://tailwindcss.com/docs/guides/vite#vue</a>)</li>
</ul>



<h2 class="wp-block-heading" id="h-i-install-flyonui">I. Install FlyonUI</h2>



<p class="wp-block-paragraph">I&#8217;m going to use the application I created for my previous blog, which you can find here: <a href="https://www.dbi-services.com/blog/vue-creating-an-awesome-parallax-effect-with-swiper/">https://www.dbi-services.com/blog/vue-creating-an-awesome-parallax-effect-with-swiper/</a>. To add FlyonUI, open your terminal in the project’s root folder and run:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
npm install flyon-ui
</pre></div>


<h2 class="wp-block-heading" id="h-ii-configure-flyonui-in-your-vue-app">II. Configure FlyonUI in Your Vue App</h2>



<p class="wp-block-paragraph">Now that FlyonUI is installed, let’s import it to make its components available across the app. Open <code>tailwind.config.js</code> and edit the file with the following code:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
/** @type {import(&#039;tailwindcss&#039;).Config} */
export default {
  content: &#x5B;
    &quot;./index.html&quot;,
    &quot;./src/**/*.{vue,js,ts,jsx,tsx}&quot;,
    &quot;./node_modules/flyonui/dist/js/*.js&quot;,
  ],
  theme: {
    extend: {},
  },
  plugins: &#x5B;
    require(&#039;flyonui&#039;),
    require(&#039;flyonui/plugin&#039;)
  ],
}
</pre></div>


<p class="wp-block-paragraph">Open <code>main.ts</code> and add the following code:</p>



<pre class="wp-block-code"><code>import "flyonui/flyonui";</code></pre>



<h2 class="wp-block-heading" id="h-iii-add-a-reinitialization-helper">III. Add a reinitialization helper </h2>



<p class="wp-block-paragraph">Add code in your route file (.<code>/src/router/index.ts</code>) to reinitialize components each time the page is refreshed.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
import { createRouter, createWebHistory } from &#039;vue-router&#039;
import HomeView from &#039;../views/HomeView.vue&#039;

import { type IStaticMethods } from &quot;flyonui/flyonui&quot;;
declare global {
  interface Window {
    HSStaticMethods: IStaticMethods;
  }
}

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: &#x5B;
    {
      path: &#039;/&#039;,
      name: &#039;home&#039;,
      component: HomeView
    },
    {
      path: &#039;/about&#039;,
      name: &#039;about&#039;,
      // route level code-splitting
      // this generates a separate chunk (About.&#x5B;hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () =&gt; import(&#039;../views/AboutView.vue&#039;)
    }
  ]
})

router.afterEach((to, from, failure) =&gt; {
  if (!failure) {
    setTimeout(() =&gt; {
      window.HSStaticMethods.autoInit();
    }, 100)
  }
});

export default router
</pre></div>


<h2 class="wp-block-heading" id="h-iv-using-flyonui-components">IV. Using FlyonUI Components</h2>



<p class="wp-block-paragraph">FlyonUI components are simple and straightforward, so let’s jump into using a few popular ones.</p>



<h3 class="wp-block-heading" id="h-example-1-button">Example 1: Button</h3>



<p class="wp-block-paragraph">Now you can use FlyonUI components throughout your Vue application. Here’s an example of using a FlyonUI responsive button:</p>



<p class="wp-block-paragraph">&lt;button class=&#8221;btn btn-primary max-sm:btn-sm lg:btn-lg&#8221;&gt;Responsive&lt;/button&gt;</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="1024" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-90-1024x1024.png" alt="" class="wp-image-35568" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-90-1024x1024.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-90-300x300.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-90-150x150.png 150w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-90-768x767.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-90.png 1274w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading" id="h-example-2-add-a-card-component">Example 2: Add a card component</h3>



<p class="wp-block-paragraph">We’ll make it simple and place the card structure below the title of our swiper slide. You can add this code to your component:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
        &lt;div class=&quot;card group hover:shadow sm:max-w-sm&quot;&gt;
          &lt;figure&gt;
            &lt;img
              src=&quot;https://cdn.flyonui.com/fy-assets/components/card/image-8.png&quot;
              alt=&quot;Shoes&quot;
              class=&quot;transition-transform duration-500 group-hover:scale-110&quot;
            /&gt;
          &lt;/figure&gt;
          &lt;div class=&quot;card-body&quot;&gt;
            &lt;h5 class=&quot;card-title mb-2.5&quot;&gt;Card title&lt;/h5&gt;
            &lt;p class=&quot;mb-6&quot;&gt;
              Nike Air Max is a popular line of athletic shoes that feature
              Nike&#039;s signature Air cushioning technology in the sole.
            &lt;/p&gt;
            &lt;div class=&quot;card-actions&quot;&gt;
              &lt;button class=&quot;btn btn-primary&quot;&gt;Buy Now&lt;/button&gt;
              &lt;button class=&quot;btn btn-secondary btn-soft&quot;&gt;Add to cart&lt;/button&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
</pre></div>


<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="1016" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-91-1024x1016.png" alt="" class="wp-image-35570" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-91-1024x1016.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-91-300x298.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-91-150x150.png 150w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-91-768x762.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-91.png 1278w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading" id="h-example-3-use-radio-buttons-to-control-css-theme">Example 3: Use radio buttons to control CSS theme</h3>



<p class="wp-block-paragraph">FlyonUI offers a range of pre-built themes that make it easy to customize the look and feel of your app. Each theme provides a cohesive color scheme applied across all FlyonUI components. To use a theme, simply add its name to your <code>tailwind.config.js</code>.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
/** @type {import(&#039;tailwindcss&#039;).Config} */
export default {
  content: &#x5B;
    &quot;./index.html&quot;,
    &quot;./src/**/*.{vue,js,ts,jsx,tsx}&quot;,
    &quot;./node_modules/flyonui/dist/js/*.js&quot;,
  ],
  theme: {
    extend: {},
  },
  flyonui: {
    themes: &#x5B;&quot;light&quot;, &quot;dark&quot;, &quot;gourmet&quot;, &quot;corporate&quot;, &quot;luxury&quot;, &quot;soft&quot;]
  },
  plugins: &#x5B;
    require(&#039;flyonui&#039;),
    require(&#039;flyonui/plugin&#039;)
  ],
}
</pre></div>


<p class="wp-block-paragraph">Now let&#8217;s add the radio buttons onto our slide and verify that the theme of our card is changing:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
        &lt;div class=&quot;join join-horizontal pb-3&quot;&gt;
          &lt;input
            type=&quot;radio&quot;
            name=&quot;theme-buttons&quot;
            class=&quot;btn theme-controller join-item&quot;
            aria-label=&quot;Default&quot;
            value=&quot;default&quot;
            checked
          /&gt;
          &lt;input
            type=&quot;radio&quot;
            name=&quot;theme-buttons&quot;
            class=&quot;btn theme-controller join-item&quot;
            aria-label=&quot;Corporate&quot;
            value=&quot;corporate&quot;
          /&gt;
          &lt;input
            type=&quot;radio&quot;
            name=&quot;theme-buttons&quot;
            class=&quot;btn theme-controller join-item&quot;
            aria-label=&quot;Gourmet&quot;
            value=&quot;gourmet&quot;
          /&gt;
        &lt;/div&gt;
</pre></div>


<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="1020" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-92-1024x1020.png" alt="" class="wp-image-35574" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-92-1024x1020.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-92-300x300.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-92-150x150.png 150w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-92-768x765.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-92.png 1275w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Well, without big surprise, this is working as expected, simple and easy to use. I like how the Gourmet theme is even applying rounded class to our buttons.</p>



<h2 class="wp-block-heading" id="h-v-advanced-features">V. Advanced Features</h2>



<p class="wp-block-paragraph">FlyonUI also offers advanced components, such as tables, charts, and timeline, to build complex interfaces quickly. Refer to the FlyonUI documentation for detailed customization options, methods, and event handling for each component.</p>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p class="wp-block-paragraph">FlyonUI seems like an excellent library for Vue.js developers looking to create modern, responsive, and interactive UIs without a significant performance hit. In this guide, we’ve covered the basics of integrating FlyonUI into your Vue project and using several key components. </p>



<p class="wp-block-paragraph">Still, FlyonUI is a fresh and evolving library, and it&#8217;s clear that the team is still working to expand its capabilities. As you may notice on the official website, some sections are marked as &#8220;coming soon.&#8221; This means there&#8217;s more to look forward to as the library grows and introduces new features. So, stay tuned for updates, enjoy exploring the current components, a big thanks to the developers and have fun testing out FlyonUI!</p>
<p>L’article <a href="https://www.dbi-services.com/blog/exploring-flyonui-with-vue-js-a-fresh-and-evolving-library/">Exploring FlyonUI with Vue.js: A Fresh and Evolving Library</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/exploring-flyonui-with-vue-js-a-fresh-and-evolving-library/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Vue &#8211; Creating an Awesome Parallax Effect with Swiper</title>
		<link>https://www.dbi-services.com/blog/vue-creating-an-awesome-parallax-effect-with-swiper/</link>
					<comments>https://www.dbi-services.com/blog/vue-creating-an-awesome-parallax-effect-with-swiper/#respond</comments>
		
		<dc:creator><![CDATA[Joan Frey]]></dc:creator>
		<pubDate>Thu, 31 Oct 2024 07:06:31 +0000</pubDate>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[vue]]></category>
		<category><![CDATA[vue-awesome-swiper]]></category>
		<category><![CDATA[vuejs]]></category>
		<category><![CDATA[vuetify]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=35481</guid>

					<description><![CDATA[<p>Adding a parallax effect to your website brings a smooth, professional feel, making it more interactive and visually appealing. With Swiper for Vue, you can easily implement parallax effects for a slideshow. In this guide, we’ll create a parallax effect using Vue 3 and explore additional features to enhance this effect. I. Create a New [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/vue-creating-an-awesome-parallax-effect-with-swiper/">Vue &#8211; Creating an Awesome Parallax Effect with Swiper</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Adding a parallax effect to your website brings a smooth, professional feel, making it more interactive and visually appealing. With <strong>Swiper</strong> for Vue, you can easily implement parallax effects for a slideshow. In this guide, we’ll create a parallax effect using Vue 3 and explore additional features to enhance this effect.</p>



<h2 class="wp-block-heading" id="h-i-create-a-new-vue-3-project">I. Create a New Vue 3 Project</h2>



<p class="wp-block-paragraph">Let&#8217;s start from scratch to create a Vue 3 app and set up <strong>Swiper</strong> to create a parallax slider. Here’s a step-by-step guide to set up the environment and build an awesome parallax effect.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
blogger@PC MINGW64 ~/Documents/Blogs/parallax-vue-awesome
$ npm init vue@latest my-parallax-app
Need to install the following packages:
  create-vue@3.11.2
Ok to proceed? (y) y

Vue.js - The Progressive JavaScript Framework

√ Add TypeScript? ... No / Yes
√ Add JSX Support? ... No / Yes
√ Add Vue Router for Single Page Application development? ... No / Yes
√ Add Pinia for state management? ... No / Yes
√ Add Vitest for Unit Testing? ... No / Yes
√ Add an End-to-End Testing Solution? » No
√ Add ESLint for code quality? ... No / Yes
√ Add Prettier for code formatting? ... No / Yes
√ Add Vue DevTools 7 extension for debugging? (experimental) ... No / Yes

Scaffolding project in C:\Users\blogger\Documents\Blogs\parallax-vue-awesome\my-parallax-app...

Done. Now run:

  cd my-parallax-app
  npm install
  npm run format
  npm run dev
</pre></div>


<h2 class="wp-block-heading" id="h-ii-install-vue-swiper">II. Install Vue Swiper</h2>



<p class="wp-block-paragraph">Next, install <strong>Swiper</strong>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
npm install swiper
</pre></div>


<h2 class="wp-block-heading" id="h-iii-set-up-the-project-structure">III. Set Up the Project Structure</h2>



<p class="wp-block-paragraph">Inside the project, let’s create a new component for our parallax slider. We’ll place this in the <code>components</code> folder for organization.</p>



<p class="wp-block-paragraph">Create a file named <code>AwesomeParallax.vue</code> in the <code>src/components</code> folder:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
src
└── components
    └── ParallaxSlider.vue
</pre></div>


<p class="wp-block-paragraph">I also added the pictures that I&#8217;ll be using for the Parallax into the <code>assets</code> directory (Pictures are mine 📷). In the end, you should have at least the following structure:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
src
├── assets
│   ├── parallax1.jpg
├── components
│   └── AwesomeParallax.vue
├── views
│   └── HomeView.vue
└── App.vue
</pre></div>


<h2 class="wp-block-heading" id="h-iv-create-the-parallax-slider-component">IV. Create the Parallax Slider Component</h2>



<p class="wp-block-paragraph">Add the following code to Awesome<code>Parallax.vue</code>. This component will import the required Swiper modules for navigation, pagination, and the parallax effect. You can find the official documentation from Swiper here: <a href="https://swiperjs.com/demos#parallax" target="_blank" rel="noreferrer noopener">https://swiperjs.com/demos#parallax</a>.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&lt;template&gt;
  &lt;swiper
    :style=&quot;{
      &#039;--swiper-navigation-color&#039;: &#039;#fff&#039;,
      &#039;--swiper-pagination-color&#039;: &#039;#fff&#039;,
    }&quot;
    :speed=&quot;600&quot;
    :parallax=&quot;true&quot;
    :pagination=&quot;{
      clickable: true,
    }&quot;
    :navigation=&quot;true&quot;
    :modules=&quot;modules&quot;
    class=&quot;mySwiper&quot;
    data-swiper-parallax-x
  &gt;
    &lt;template v-slot:container-start&gt;
      &lt;div class=&quot;parallax-bg&quot; data-swiper-parallax=&quot;-23%&quot;&gt;&lt;/div&gt;
    &lt;/template&gt;
    &lt;swiper-slide&gt;
      &lt;div class=&quot;title&quot; data-swiper-parallax=&quot;-300&quot;&gt;Slide 1&lt;/div&gt;
      &lt;div class=&quot;subtitle&quot; data-swiper-parallax=&quot;-200&quot;&gt;Subtitle&lt;/div&gt;
      &lt;div class=&quot;text&quot; data-swiper-parallax=&quot;-100&quot;&gt;
        &lt;p&gt;
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam
          dictum mattis velit, sit amet faucibus felis iaculis nec. Nulla
          laoreet justo vitae porttitor porttitor. Suspendisse in sem justo.
          Integer laoreet magna nec elit suscipit, ac laoreet nibh euismod.
          Aliquam hendrerit lorem at elit facilisis rutrum. Ut at ullamcorper
          velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut
          libero. Aenean feugiat non eros quis feugiat.
        &lt;/p&gt;
      &lt;/div&gt; &lt;/swiper-slide
    &gt;&lt;swiper-slide&gt;
      &lt;div class=&quot;title&quot; data-swiper-parallax=&quot;-300&quot;&gt;Slide 2&lt;/div&gt;
      &lt;div class=&quot;subtitle&quot; data-swiper-parallax=&quot;-200&quot;&gt;Subtitle&lt;/div&gt;
      &lt;div class=&quot;text&quot; data-swiper-parallax=&quot;-100&quot;&gt;
        &lt;p&gt;
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam
          dictum mattis velit, sit amet faucibus felis iaculis nec. Nulla
          laoreet justo vitae porttitor porttitor. Suspendisse in sem justo.
          Integer laoreet magna nec elit suscipit, ac laoreet nibh euismod.
          Aliquam hendrerit lorem at elit facilisis rutrum. Ut at ullamcorper
          velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut
          libero. Aenean feugiat non eros quis feugiat.
        &lt;/p&gt;
      &lt;/div&gt; &lt;/swiper-slide
    &gt;&lt;swiper-slide&gt;
      &lt;div class=&quot;title&quot; data-swiper-parallax=&quot;-300&quot;&gt;Slide 3&lt;/div&gt;
      &lt;div class=&quot;subtitle&quot; data-swiper-parallax=&quot;-200&quot;&gt;Subtitle&lt;/div&gt;
      &lt;div class=&quot;text&quot; data-swiper-parallax=&quot;-100&quot;&gt;
        &lt;p&gt;
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam
          dictum mattis velit, sit amet faucibus felis iaculis nec. Nulla
          laoreet justo vitae porttitor porttitor. Suspendisse in sem justo.
          Integer laoreet magna nec elit suscipit, ac laoreet nibh euismod.
          Aliquam hendrerit lorem at elit facilisis rutrum. Ut at ullamcorper
          velit. Nulla ligula nisi, imperdiet ut lacinia nec, tincidunt ut
          libero. Aenean feugiat non eros quis feugiat.
        &lt;/p&gt;
      &lt;/div&gt;
    &lt;/swiper-slide&gt;
  &lt;/swiper&gt;
&lt;/template&gt;
&lt;script lang=&quot;ts&quot;&gt;
// Import Swiper Vue.js components
import { Swiper, SwiperSlide } from &#039;swiper/vue&#039;

// Import Swiper styles
import &#039;swiper/css&#039;

import &#039;swiper/css/pagination&#039;
import &#039;swiper/css/navigation&#039;

// import required modules
import { Parallax, Pagination, Navigation } from &#039;swiper/modules&#039;

export default {
  components: {
    Swiper,
    SwiperSlide,
  },
  setup() {
    return {
      modules: &#x5B;Parallax, Pagination, Navigation],
    }
  },
}
&lt;/script&gt;

&lt;style&gt;
#app {
  height: 100%;
  display: contents;
}
html,
body {
  position: relative;
  height: 100%;
}

body {
  background: #eee;
  font-family:
    Helvetica Neue,
    Helvetica,
    Arial,
    sans-serif;
  font-size: 14px;
  color: #000;
  margin: 0;
  padding: 0;
}

.swiper {
  width: 100%;
  height: 100%;
  background: #000;
}

.swiper-slide {
  font-size: 18px;
  color: #fff;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  padding: 40px 60px;
}

.parallax-bg {
  position: absolute;
  left: 0;
  top: 0;
  width: 130%;
  height: 100%;
  -webkit-background-size: cover;
  background-size: cover;
  background-position: center;
  background-image: url(&#039;../assets/parallax1.jpg&#039;);
}

.swiper-slide .title {
  font-size: 41px;
  font-weight: 300;
}

.swiper-slide .subtitle {
  font-size: 21px;
}

.swiper-slide .text {
  font-size: 14px;
  max-width: 400px;
  line-height: 1.3;
}
&lt;/style&gt;
</pre></div>


<p class="wp-block-paragraph">To achieve a full-screen parallax effect, I used <code>display: contents;</code> on the <code>#app</code> element. This allows the background to stretch across the viewport without being constrained by the parent element&#8217;s dimensions.</p>



<p class="wp-block-paragraph">However, setting the <code>.parallax-bg</code> width to <code>130%</code> can cause height issues, as the image might not scale proportionately, leading to excess height. Still, this is needed to achieve the parallax effect in our case, so it means that you need to choose your background image knowing that it is going to be cropped.</p>



<h2 class="wp-block-heading" id="h-v-use-the-component-in-homeview-vue">V. Use the Component in HomeView.vue</h2>



<p class="wp-block-paragraph">Now that we’ve created the Awesome<code>Parallax</code> component, we can add it to <code>HomeView.vue</code> to display it on the main page.</p>



<p class="wp-block-paragraph">Open <code>src/HomeView.vue</code> and replace its content with the following:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&lt;template&gt;
    &lt;AwesomeParallax /&gt;
&lt;/template&gt;

&lt;script setup lang=&quot;ts&quot;&gt;
import AwesomeParallax from &#039;@/components/AwesomeParallax.vue&#039;
&lt;/script&gt;
</pre></div>


<p class="wp-block-paragraph">Then open <code>src/App.vue</code> and replace its content with the following:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&lt;template&gt;
  &lt;HomeView /&gt;
&lt;/template&gt;

&lt;script setup lang=&quot;ts&quot;&gt;
import HomeView from &#039;@/views/HomeView.vue&#039;
&lt;/script&gt;
</pre></div>


<h2 class="wp-block-heading" id="h-vi-run-the-application">VI. Run the application</h2>



<p class="wp-block-paragraph">With everything in place, you can start the development server to see the parallax effect in action:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
npm run dev
</pre></div>


<p class="wp-block-paragraph">Navigate to <code>http://localhost:3000</code> in your browser to see your parallax slider. You should see a smoothly animated slider with a parallax background and Swiper&#8217;s navigation and pagination controls.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="482" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-88-1024x482.png" alt="" class="wp-image-35504" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-88-1024x482.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-88-300x141.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-88-768x361.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-88-1536x723.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-88.png 1917w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading" id="h-vii-enhancing-the-parallax-effect">VII. Enhancing the Parallax Effect</h2>



<p class="wp-block-paragraph">Now that you have a working parallax slider, you can experiment with additional features, such as:</p>



<ul class="wp-block-list">
<li><strong>Slide Transition Duration</strong>: Customize the transition duration for smoother animations by editing the <code>:speed</code> prop adjustments on the <code>swiper</code> component.</li>



<li><strong>Center the text</strong>: To center the text, title, and subtitle in the middle of your screen, you can use flexbox properties on the <code>.swiper-slide</code> elements. Here’s how you can adjust your CSS:</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
.swiper-slide {
  font-size: 18px;
  color: #fff;
  box-sizing: border-box;
  padding: 40px 60px;
  
  /* Flexbox properties for centering */
  display: flex;
  flex-direction: column; /* Stack elements vertically */
  justify-content: center; /* Center vertically */
  align-items: center; /* Center horizontally */
}

.swiper-slide .title {
  font-size: 41px;
  font-weight: 300;
  text-align: center; /* Center text */
}

.swiper-slide .subtitle {
  font-size: 21px;
  text-align: center; /* Center text */
}

.swiper-slide .text {
  font-size: 14px;
  max-width: 400px;
  line-height: 1.3;
  text-align: center; /* Center text */
}
</pre></div>


<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="501" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-89-1024x501.png" alt="" class="wp-image-35512" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-89-1024x501.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-89-300x147.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-89-768x376.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-89-1536x752.png 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-89.png 1915w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li><strong>Adding Image Gradients</strong>: Layering gradients over the background images to create depth. To apply a gradient overlay on top of your background image while keeping the image itself, you can modify the <code>.parallax-bg</code> class to use both the background image and the gradient. Here&#8217;s how you can do it:</li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,8,9]; title: ; notranslate">
.parallax-bg {
  position: absolute;
  left: 0;
  top: 0;
  width: 130%;
  height: 100%;
  background-image:
    linear-gradient(to right, rgba(255, 128, 0, 0.3), rgba(0, 0, 0, 0.9)),
    /* Gradient overlay */ url(&#039;../assets/parallax1.jpg&#039;); /* Background image */
  background-size: cover;
  background-position: center;
}
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1920" height="945" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/chrome_W0asXYnhZN-1.gif" alt="" class="wp-image-35520" /></figure>



<ul class="wp-block-list">
<li><strong>Vertical parallax</strong>: Add the props <code>:direction="'vertical'"</code> to create a vertical parallax. In this case, don&#8217;t forget to edit the class parallax-bg to adjust the height and width property as needed:<br></li>
</ul>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; highlight: [13]; title: ; notranslate">
&lt;swiper
    :style=&quot;{
      &#039;--swiper-navigation-color&#039;: &#039;#fff&#039;,
      &#039;--swiper-pagination-color&#039;: &#039;#fff&#039;,
    }&quot;
    :speed=&quot;600&quot;
    :parallax=&quot;true&quot;
    :pagination=&quot;{
      clickable: true,
    }&quot;
    :navigation=&quot;true&quot;
    :modules=&quot;modules&quot;
    :direction=&quot;&#039;vertical&#039;&quot;
    class=&quot;mySwiper&quot;
    data-swiper-parallax-x
  &gt;

.parallax-bg {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 130%;
  background-image:
    linear-gradient(to right, rgba(255, 128, 0, 0.3), rgba(0, 0, 0, 0.9)),
    /* Gradient overlay */ url(&#039;../assets/parallax1.jpg&#039;); /* Background image */
  background-size: cover;
  background-position: center;
}
</pre></div>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="2560" height="1272" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/chrome_Hvv9LrSsEA-ezgif.com-split.gif" alt="" class="wp-image-35532" style="width:840px;height:auto" /></figure>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p class="wp-block-paragraph">This guide demonstrated how to set up a Vue 3 application with a parallax effect, creating visually engaging user experiences. You can further enhance the parallax by adjusting animation speeds, or even incorporating dynamic slide content. This setup is highly flexible for building beautiful, interactive sliders in Vue apps. Don&#8217;t forget to check Swiper API to learn more about what is doable with this component.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/vue-creating-an-awesome-parallax-effect-with-swiper/">Vue &#8211; Creating an Awesome Parallax Effect with Swiper</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/vue-creating-an-awesome-parallax-effect-with-swiper/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Creating a Global and Custom Snackbar in Vue 3 Using Vuetify and Pinia</title>
		<link>https://www.dbi-services.com/blog/creating-a-global-snackbar-in-a-vue-3-application-using-vuetify-and-pinia/</link>
					<comments>https://www.dbi-services.com/blog/creating-a-global-snackbar-in-a-vue-3-application-using-vuetify-and-pinia/#respond</comments>
		
		<dc:creator><![CDATA[Joan Frey]]></dc:creator>
		<pubDate>Thu, 17 Oct 2024 16:25:20 +0000</pubDate>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[global]]></category>
		<category><![CDATA[Global page]]></category>
		<category><![CDATA[Pinia]]></category>
		<category><![CDATA[snackbar]]></category>
		<category><![CDATA[vue]]></category>
		<category><![CDATA[vuejs]]></category>
		<category><![CDATA[vuetify]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=35281</guid>

					<description><![CDATA[<p>In this post, we&#8217;ll walk through how to implement a Global and Custom Snackbar in Vue 3 with Vuetify and Pinia for state management. We&#8217;ll set it up so that you can easily trigger the Snackbar from any component in your app. I. Set Up Your Vue App with Vuetify CLI To start, we’ll create [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/creating-a-global-snackbar-in-a-vue-3-application-using-vuetify-and-pinia/">Creating a Global and Custom Snackbar in Vue 3 Using Vuetify and Pinia</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In this post, we&#8217;ll walk through how to implement a Global and Custom Snackbar in Vue 3 with Vuetify and Pinia for state management. We&#8217;ll set it up so that you can easily trigger the Snackbar from any component in your app.</p>



<h2 class="wp-block-heading" id="h-i-set-up-your-vue-app-with-vuetify-cli">I. Set Up Your Vue App with Vuetify CLI</h2>



<p class="wp-block-paragraph">To start, we’ll create our Vue 3 app using the Vuetify CLI. This CLI simplifies the process of integrating Vuetify and sets up your project structure. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
npm create vuetify@latest
</pre></div>


<p class="wp-block-paragraph">Follow the prompts to choose your project settings and install dependencies. As we are going to use Pinia, don&#8217;t forget to choose this option. I decided to keep all the generated files to keep it simple and straightforward, but you can clean the code that was automatically generated. I just cleaned the component HelloWorld.vue, that now looks like this:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&lt;template&gt;
  &lt;v-container class=&quot;fill-height&quot;&gt;
    &lt;v-responsive class=&quot;align-centerfill-height mx-auto&quot; max-width=&quot;900&quot;&gt;
    &lt;/v-responsive&gt;
  &lt;/v-container&gt;
&lt;/template&gt;

&lt;script setup lang=&quot;ts&quot;&gt;
&lt;/script&gt;
</pre></div>


<h2 class="wp-block-heading" id="h-ii-set-up-the-pinia-store-for-snackbar">II. Set Up the Pinia Store for Snackbar</h2>



<p class="wp-block-paragraph">Next, create a Pinia store (<code>SnackbarStore.ts</code>) to handle the state of the Snackbar (open/close, message, status, etc.). This will allow you to trigger the Snackbar from anywhere in the app.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
// SnackbarStore.ts inside stores directory

import { defineStore } from &#039;pinia&#039;
import { Ref, ref } from &#039;vue&#039;

export interface CustomAction {
  actionName: string;
  link: any;
}

export const useSnackbarStore = defineStore(&#039;SnackbarStore&#039;, () =&gt; {
  const isOpen = ref(false)
  const message = ref(&#039;&#039;)
  const status = ref(&#039;error&#039;)
  const customActions: Ref&lt;CustomAction&#x5B;]&gt; = ref(&#x5B;])

  const showSnackbar = (msg: string, st: string, optionalActions?: CustomAction&#x5B;]) =&gt; { // Change to accept an array
    message.value = msg
    status.value = st
    isOpen.value = true
    if (optionalActions) {
      customActions.value.push(...optionalActions) // Spread the array into customActions
    }
    if (status.value.toLowerCase() === &#039;success&#039;) {
      setTimeout(() =&gt; {
        closeSnackbar()
      }, 2500)
    }
  }

  function closeSnackbar () {
    isOpen.value = false
    customActions.value.splice(0, customActions.value.length) // Clear custom actions
  }

  return { isOpen, message, status, showSnackbar, closeSnackbar, customActions }
})
</pre></div>


<h2 class="wp-block-heading" id="h-iii-create-the-snackbar-component">III. Create the Snackbar Component</h2>



<p class="wp-block-paragraph">We&#8217;ll create a reusable Snackbar component (<code>GlobalSnackbar.vue</code>) that can be used anywhere in your application.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&lt;template&gt;
  &lt;div class=&quot;text-center&quot;&gt;
    &lt;v-snackbar
      v-model=&quot;snackbarRef&quot;
      class=&quot;customSnackbar&quot;
      :color=&quot;snackbarStatus&quot;
      variant=&quot;outlined&quot;
      vertical
    &gt;
      &lt;div class=&quot;text-subtitle-1 pb-2 title-custom&quot;&gt;{{ snackbarStatus }}&lt;/div&gt;

      &lt;p&gt;{{ errorMsg }}&lt;/p&gt;

      &lt;template #actions&gt;
        &lt;v-btn
          v-for=&quot;act of snackbarActions&quot;
          :key=&quot;act.actionName&quot;
          :to=&quot;act.link&quot;
        &gt;
          {{ act.actionName }}
        &lt;/v-btn&gt;
        &lt;v-btn variant=&quot;tonal&quot; @click=&quot;closeSnackbar&quot;&gt; Close &lt;/v-btn&gt;
      &lt;/template&gt;
    &lt;/v-snackbar&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script lang=&quot;ts&quot; setup&gt;
  import { toRef } from &#039;vue&#039;
  import { CustomAction, useSnackbarStore } from &#039;@/stores/SnackbarStore&#039;

  // Define the properties that the component will receive
  const props = defineProps({
    snackbarShow: Boolean,
    errorMsg: String,
    snackbarStatus: String,
    snackbarActions: {
      type: Array as () =&gt; CustomAction&#x5B;], // Custom type to define snackbar actions
    },
  })

  const snackbar = useSnackbarStore()
  const snackbarRef = toRef(props, &#039;snackbarShow&#039;)

  const closeSnackbar = () =&gt; {
    snackbar.closeSnackbar()
  }
&lt;/script&gt;

&lt;style&gt;
.customSnackbar.v-snackbar--vertical .v-snackbar__wrapper {
  background-color: rgb(238, 238, 238);
}
.title-custom {
  text-transform: uppercase !important;
}
&lt;/style&gt;
</pre></div>


<h2 class="wp-block-heading" id="h-iv-modify-app-vue-to-include-the-global-snackbar">IV. Modify <code>App.vue</code> to Include the Global Snackbar</h2>



<p class="wp-block-paragraph">To ensure the Snackbar is available across your entire app, import it into <code>App.vue</code> and pass the required props from the <code>SnackbarStore</code>.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&lt;template&gt;
  &lt;v-app&gt;
    &lt;v-main&gt;
      &lt;router-view /&gt;
    &lt;/v-main&gt;
    &lt;GlobalSnackbar
      :error-msg=&quot;snackbar.message&quot;
      :snackbar-actions=&quot;snackbar.customActions&quot;
      :snackbar-show=&quot;snackbar.isOpen&quot;
      :snackbar-status=&quot;snackbar.status&quot;
    /&gt;
  &lt;/v-app&gt;
&lt;/template&gt;

&lt;script lang=&quot;ts&quot; setup&gt;
  import { useSnackbarStore } from &#039;@/stores/SnackbarStore&#039;
  const snackbar = useSnackbarStore()
&lt;/script&gt;
</pre></div>


<p class="wp-block-paragraph">This step is essential to making sure the <code>GlobalSnackbar</code> component is part of the root layout and is always available to display messages.</p>



<h2 class="wp-block-heading" id="h-v-using-the-snackbar-in-a-component">V. Using the Snackbar in a Component</h2>



<p class="wp-block-paragraph">Now, let&#8217;s demonstrate how to trigger the Snackbar from any component. For this example, we’ll use a button that, when clicked, shows a success message with an optional action. We can edit HelloWord.vue with following code.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
&lt;template&gt;
  &lt;v-container class=&quot;fill-height&quot;&gt;
    &lt;v-responsive class=&quot;align-centerfill-height mx-auto&quot; max-width=&quot;900&quot;&gt;
      &lt;v-btn @click=&quot;openSnackbar()&quot;&gt;test&lt;/v-btn&gt;
    &lt;/v-responsive&gt;
  &lt;/v-container&gt;
&lt;/template&gt;

&lt;script setup lang=&quot;ts&quot;&gt;
  import { useSnackbarStore } from &#039;@/stores/SnackbarStore&#039;
  const snackbar = useSnackbarStore()

  const openSnackbar = () =&gt; {
    snackbar.showSnackbar(&#039;Show snackbar&#039;, &#039;success&#039;, &#x5B;
      { actionName: &#039;Go to Home&#039;, link: &#039;/home&#039; },
      { actionName: &#039;Go to Test&#039;, link: &#039;/test&#039; },
    ])
  }
&lt;/script&gt;
</pre></div>


<h2 class="wp-block-heading" id="h-vi-testing-the-snackbar">VI. Testing the Snackbar</h2>



<p class="wp-block-paragraph">Once everything is in place, try clicking the button to trigger the Snackbar. You should see the Snackbar with a success message and an optional action button appear at the bottom of the screen. And Logically, if you click on one of the custom action buttons, it will redirect you properly, but you will obviously see a blank page, as the view doesn&#8217;t exist.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="521" height="493" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-49.png" alt="" class="wp-image-35295" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-49.png 521w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-49-300x284.png 300w" sizes="auto, (max-width: 521px) 100vw, 521px" /></figure>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p class="wp-block-paragraph">In this post, we created a global and custom Snackbar component in Vue 3 using Vuetify and Pinia. We set it up so that it can be easily triggered from anywhere in the app. By managing its state with Pinia, we keep the logic centralized and clean, making the Snackbar reusable across the entire application.</p>



<p class="wp-block-paragraph">Feel free to adjust the styling and functionality of the Snackbar to suit your needs. Now you have a flexible, easy-to-use notification system for your Vue 3 project!</p>
<p>L’article <a href="https://www.dbi-services.com/blog/creating-a-global-snackbar-in-a-vue-3-application-using-vuetify-and-pinia/">Creating a Global and Custom Snackbar in Vue 3 Using Vuetify and Pinia</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/creating-a-global-snackbar-in-a-vue-3-application-using-vuetify-and-pinia/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Manage Feathers.js authentication in Swagger UI</title>
		<link>https://www.dbi-services.com/blog/manage-feathers-js-authentication-in-swagger-ui/</link>
					<comments>https://www.dbi-services.com/blog/manage-feathers-js-authentication-in-swagger-ui/#respond</comments>
		
		<dc:creator><![CDATA[Nicolas Meunier]]></dc:creator>
		<pubDate>Fri, 30 Aug 2024 14:16:29 +0000</pubDate>
				<category><![CDATA[Development & Performance]]></category>
		<category><![CDATA[Web]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[Swagger UI]]></category>
		<category><![CDATA[TypeScript]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=34600</guid>

					<description><![CDATA[<p>In addition to my previous articles Add a UI to explore the Feathers.js API and Add authentication in a Feathers.js REST API, today, I will explain how to manage Feathers.js authentication in Swagger UI and make it simple for our users. After enabling authentication on API methods, as a result, using Swagger UI for testing [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/manage-feathers-js-authentication-in-swagger-ui/">Manage Feathers.js authentication in Swagger UI</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In addition to my previous articles <a href="https://www.dbi-services.com/blog/add-a-ui-to-explore-the-feathers-js-api/">Add a UI to explore the Feathers.js API</a> and <a href="https://www.dbi-services.com/blog/add-authentication-in-a-feathers-js-rest-api/">Add authentication in a Feathers.js REST API</a>, today, I will explain how to manage Feathers.js authentication in <a href="https://swagger.io/tools/swagger-ui/">Swagger UI</a> and make it simple for our users.</p>



<p class="wp-block-paragraph">After enabling authentication on API methods, as a result, using Swagger UI for testing is difficult, if not impossible. To solve this problem, Swagger needs to support authentication.</p>



<p class="wp-block-paragraph">In this article, I will reuse the code from my previous articles.</p>



<h2 class="wp-block-heading" id="h-first-step-add-authentication-specifications-to-the-application">First step: add authentication specifications to the application</h2>



<p class="wp-block-paragraph">For Swagger UI to handle API authentication, I need to update the specifications in the app.ts file. Therefore, in the specs object, I add the components and security parts:</p>



<pre class="wp-block-code"><code>app.configure(
  swagger({
    specs: {
      info: {
        title: 'Workshop API ',
        description: 'Workshop API rest services',
        version: '1.0.0'
      },
      components: {
        securitySchemes: {
          BearerAuth: {
            type: 'http',
            scheme: 'bearer',
          },
        },
      },
      security: &#091;{ BearerAuth: &#091;] }],
    },
    ui: swagger.swaggerUI({ 
      docsPath: '/docs',
    })
  })
)</code></pre>



<p class="wp-block-paragraph">This new configuration, general to the API, tells Swagger that authenticated methods will use a Bearer token.</p>



<h2 class="wp-block-heading" id="h-second-step-define-documentation-for-authentication-methods">Second step: define documentation for authentication methods</h2>



<p class="wp-block-paragraph">The authentication service generated by the CLI does not contain any specifications for Swagger UI, which results in an error message being displayed on the interface. So I&#8217;m adding the specifications needed by Swagger to manage authentication methods:</p>



<pre class="wp-block-code"><code>
export const authentication = (app: Application) =&gt; {
  const authentication = new AuthenticationService(app)

  authentication.register('jwt', new JWTStrategy())
  authentication.register('local', new LocalStrategy())

  // Swagger definition.
  authentication.docs = {
    idNames: {
      remove: 'accessToken',
    },
    idType: 'string',
    securities: &#091;'remove', 'removeMulti'],
    multi: &#091;'remove'],
    schemas: {
      authRequest: {
        type: 'object',
        properties: {
          strategy: { type: 'string' },
          email: { type: 'string' },
          password: { type: 'string' },
        },
      },
      authResult: {
        type: 'object',
        properties: {
          accessToken: { type: 'string' },
          authentication: {
            type: 'object',
            properties: {
              strategy: { type: 'string' },
            },
          },
          payload: {
            type: 'object',
            properties: {},
          },
          user: { $ref: '#/components/schemas/User' },
        },
      },
    },
    refs: {
      createRequest: 'authRequest',
      createResponse: 'authResult',
      removeResponse: 'authResult',
      removeMultiResponse: 'authResult',
    },
    operations: {
      remove: {
        description: 'Logout the current user',
        'parameters&#091;0].description': 'accessToken of the current user',
      },
      removeMulti: {
        description: 'Logout the current user',
        parameters: &#091;],
      },
    },
  };

  app.use('authentication', authentication)
}</code></pre>



<p class="wp-block-paragraph">The authentication.docs block defines how Swagger UI can interact with the authentication service.</p>



<h2 class="wp-block-heading" id="h-third-step-add-the-security-config-into-the-service">Third step: add the security config into the service</h2>



<p class="wp-block-paragraph">At the beginning of my service file (workshop.ts), in the docs definition, I add the list of methods that must be authenticated in the securities array:</p>



<pre class="wp-block-code"><code>docs: createSwaggerServiceOptions({
  schemas: { 
    workshopSchema, 
    workshopDataSchema, 
    workshopPatchSchema, 
    workshopQuerySchema 
  },
  docs: {
      // any options for service.docs can be added here
      description: 'Workshop service',
      securities: &#091;'find', 'get', 'create', 'update', 'patch', 'remove'],
  }</code></pre>



<h2 class="wp-block-heading" id="h-swagger-ui-authentication-tests">Swagger UI authentication tests</h2>



<p class="wp-block-paragraph">As a result of the configuration, new elements appear on the interface:</p>



<ul class="wp-block-list">
<li>The Authorize button at the top of the page</li>



<li>Padlocks at the end of authenticated method lines</li>



<li>A more complete definition of authentication service methods</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="459" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Swagger_Authentication-1024x459.png" alt="Swagger Authentication" class="wp-image-34609" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Swagger_Authentication-1024x459.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Swagger_Authentication-300x135.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Swagger_Authentication-768x344.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Swagger_Authentication.png 1492w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading" id="h-obtain-an-access-token-with-the-ui">Obtain an access token with the UI</h3>



<p class="wp-block-paragraph">First, I need to log in with my credentials to get a token. After that, the bearer token will be used in the next authenticated calls:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="522" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_request-1024x522.png" alt="Authentication request" class="wp-image-34614" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_request-1024x522.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_request-300x153.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_request-768x391.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_request.png 1462w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">The JSON object sent in the request body contains the identification information. In return, we&#8217;ll get an accessToken useful for the next calls:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="631" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_response-1024x631.png" alt="Authentication response" class="wp-image-34615" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_response-1024x631.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_response-300x185.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_response-768x473.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authentication_response.png 1440w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading" id="h-second-step-define-the-bearer-token-for-authenticated-requests">Second step, define the bearer token for authenticated requests</h3>



<p class="wp-block-paragraph">Each call to an authenticated method requires the token to be sent in the http headers. We therefore register the token in Swagger UI by clicking on the Authorize button and paste the token obtained during the authentication step:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="385" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Define_Auth_Credentials-1024x385.png" alt="Define authentication credentials" class="wp-image-34616" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Define_Auth_Credentials-1024x385.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Define_Auth_Credentials-300x113.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Define_Auth_Credentials-768x289.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Define_Auth_Credentials.png 1488w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h3 class="wp-block-heading" id="h-testing-authenticated-methods">Testing authenticated methods</h3>



<p class="wp-block-paragraph">Once the token has been entered into Swagger UI, I use the methods without worrying about authentication:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="601" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_request-1024x601.png" alt="Call authenticated method" class="wp-image-34619" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_request-1024x601.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_request-300x176.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_request-768x451.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_request.png 1465w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Swagger UI sends the token in the http headers to authenticate the request. Then the api returns the data:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="547" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_response-1024x547.png" alt="Authenticated response" class="wp-image-34620" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_response-1024x547.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_response-300x160.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_response-768x410.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/08/Authenticated_method_response.png 1446w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p class="wp-block-paragraph">Managing Feathers.js authentication in Swagger UI is entirely possible. With a little extra configuration, it becomes easy to use authentication and authenticated methods. It&#8217;s even simple to test all API methods and document their use.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/manage-feathers-js-authentication-in-swagger-ui/">Manage Feathers.js authentication in Swagger UI</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/manage-feathers-js-authentication-in-swagger-ui/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Use Dynamic Slots in Vue.js with Vuetify Data Table</title>
		<link>https://www.dbi-services.com/blog/how-to-use-dynamic-slots-in-vue-js-with-vuetify-data-table/</link>
					<comments>https://www.dbi-services.com/blog/how-to-use-dynamic-slots-in-vue-js-with-vuetify-data-table/#comments</comments>
		
		<dc:creator><![CDATA[Joan Frey]]></dc:creator>
		<pubDate>Fri, 24 May 2024 08:38:44 +0000</pubDate>
				<category><![CDATA[Web]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[replication slots]]></category>
		<category><![CDATA[TypeScript]]></category>
		<category><![CDATA[vue]]></category>
		<category><![CDATA[vuejs]]></category>
		<category><![CDATA[vuetify]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=33171</guid>

					<description><![CDATA[<p>Introduction In this tutorial, we will explore how to use dynamic slots in Vue.js with Vuetify data table. Firstly, we will start with a CompDataTable.vue component and demonstrate how to define and use custom slots to dynamically customize the content of a data table. Additionally, we will explain how the dynamic slots work. I’m not [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-use-dynamic-slots-in-vue-js-with-vuetify-data-table/">How to Use Dynamic Slots in Vue.js with Vuetify Data Table</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-introduction">Introduction</h2>



<p class="wp-block-paragraph">In this tutorial, we will explore how to use dynamic slots in Vue.js with Vuetify data table. Firstly, we will start with a CompDataTable.vue component and demonstrate how to define and use custom slots to dynamically customize the content of a data table. Additionally, we will explain how the dynamic slots work.</p>



<p class="wp-block-paragraph">I’m not going to create a Vue.js project from scratch. Instead, I will use a Vuetify playground. You can also try the content of this blog without creating a local Vue.js project. Specifically, I’m going to use the playground available for the Data table <code>Server-side paginate and sort</code> (<a href="https://play.vuetifyjs.com/#eNqNV29vszYQ/yonpAmyAc3/tijp9qxbt66T1hfTpCnNC4c4CSvByDh5lEX57s/ZGAyEir4C7n53/vnufD4WZ+tLmvrHA7UCayboPo2JoA9vCcDs6K2JIJ4gq5h6GeVHyqUc4Ojt2ZrGQYT4zEsp91KypfM3SwleKX/FzzcrBwc7StaUZ6jWb6VGwVGe+36WX3WdF9NkK3YIEUyQuI6IGVlHyRaV+q3UZJTwUFrlL4VcuvSOJD5IqgnZlxR/OqS4UxqwVEQskYykx8pqD7OblmBglGY3lZDhZxbyKBWQUXFIVRCjfcq4gDNwuoELbDjbg43RtiUcIMTlBKxphh5FBnNY5IzO+QNA0gzAfuLsf5rAv2x74MJ2C21IYsYjmgUwmNyX0g0RAUwrIL5CxHBcSlLOBI2SAIwo4gy/7UHh+6KfTSJ/0Dg+wYqSpI3F6HZSZ9FvsrhvYWFAmkW/i8VLJF5Iaxwmg7s6g+FVIKaGY0nhtklh2kXh1zAmEW+jMJwO6xQG17kYXVMwIE3htovCb1jzlK84nqrWZEyMyw94jE3RlDxGvhEWVdEZjeeQQohE9pCRZP01CnetkRmZOCtGZiVNqAIwReqbaH22TP9kcRylLG2Ny30jP33fCIoyNVX0cZkOu1g8HtKQvNNWEv3GSRn5ZutFoX4uGHddNH5nCT2FbL9qIzLuNw7M6Doady1Epr7ZgCYynnQx+YUlh9ZjO540cjI03jWJyaAtGle1OmxkZWna7BPm4svrM3ZZzYtkpySEDRXhDpwzyEvMVbdEcYe5kGH3/vkEl57ZC8fmzhNI6Fd4xXYeZdThNGPxkcL8wcBA3gJ/R3vKDsJxeg1dwSkTBK+HOThycfBg0IPvaxSuTWiyRoPc8IcOrNIiurhh/CyOQur0qshoA06+TT+/bit7rVFFzAs9ybUVetFf+u/09BH0L453fQ3MpKQOV/x8CXEc4sLqOkzGKflHXt3okSw0FZXba+CqAK4+BOocVnjO52BjlEIbfiwceMWSQfHiaVXd3aUWzst1FjC3UYIzgsyb3rFKg8qhKxNa86CrCStSgQNj74KagwLtJU9Xbf2LC5N+vxQUqvwsKGqVwtBlg6xwOnEmCpur9aSmNc2hREQiVsc5rypwBv3+FsudH/FG6pnzTeJoK8+k2qcRy6jLMSrAox5ntJRjMSFYtoqrPlIu+ahbhu1qeNFDUFAsh/G0W0yfCDKV9LQl9pluo0fZfGpmqh11G2JrkD2qZqr7VrfxM/YycL4zlrK5tZstK1nLx12dNNuuacoJu8hp1VAP0Fol+IFWlGb01vq8vDaHJJTjsjLO1Z/toXo5X83i6FQumGt0i/ZVT+5y54sdTZzimOizIZep9pDKzsv1FL4s5nJ7ho8UFfomWVWzujxVIPBUzW7ywf/Bcq04St4z/7+MJfhHdX6zwgx/IoLF8oK6/G/A25O0AsiFEnS+XKyLa438sT+8RfjIn/pTy5XhWX4DhWf9gg==">Here</a>).</p>



<h2 class="wp-block-heading" id="h-setting-up-a-vue-component-compdatatable-vue">Setting Up a Vue component CompDataTable.vue</h2>



<p class="wp-block-paragraph">First, let’s create a <code>CompDataTable.vue</code> component that uses Vuetify’s <code>v-data-table-server</code> to display data fetched from a fake API. You should have three files as follows:</p>



<p class="wp-block-paragraph">vuetify.js</p>



<pre class="wp-block-code"><code>import { createVuetify } from 'vuetify'

export const vuetify = createVuetify({
  theme: {
    defaultTheme: 'light',
    //
  },
})</code></pre>



<p class="wp-block-paragraph">App.vue</p>



<pre class="wp-block-code"><code>&lt;template&gt;
  &lt;CompDataTable&gt;&lt;/CompDataTable&gt;
&lt;/template&gt;

&lt;script setup&gt;
  import CompDataTable from './CompDataTable.vue'
&lt;/script&gt;</code></pre>



<p class="wp-block-paragraph">CompDataTable.vue</p>



<pre class="wp-block-code"><code>&lt;template&gt;
  &lt;v-data-table-server
    v-model:items-per-page="itemsPerPage"
    :headers="headers"
    :items="serverItems"
    :items-length="totalItems"
    :loading="loading"
    :search="search"
    item-value="name"
    @update:options="loadItems"
  &gt;&lt;/v-data-table-server&gt;
&lt;/template&gt;

&lt;script setup&gt;
  import { ref } from 'vue'

  const desserts = &#091;
    {
      name: 'Frozen Yogurt',
      calories: 159,
      fat: 6,
      carbs: 24,
      protein: 4,
      iron: '1',
    },
    {
      name: 'Jelly bean',
      calories: 375,
      fat: 0,
      carbs: 94,
      protein: 0,
      iron: '0',
    },
    {
      name: 'KitKat',
      calories: 518,
      fat: 26,
      carbs: 65,
      protein: 7,
      iron: '6',
    },
    {
      name: 'Eclair',
      calories: 262,
      fat: 16,
      carbs: 23,
      protein: 6,
      iron: '7',
    },
    {
      name: 'Gingerbread',
      calories: 356,
      fat: 16,
      carbs: 49,
      protein: 3.9,
      iron: '16',
    },
    {
      name: 'Ice cream sandwich',
      calories: 237,
      fat: 9,
      carbs: 37,
      protein: 4.3,
      iron: '1',
    },
    {
      name: 'Lollipop',
      calories: 392,
      fat: 0.2,
      carbs: 98,
      protein: 0,
      iron: '2',
    },
    {
      name: 'Cupcake',
      calories: 305,
      fat: 3.7,
      carbs: 67,
      protein: 4.3,
      iron: '8',
    },
    {
      name: 'Honeycomb',
      calories: 408,
      fat: 3.2,
      carbs: 87,
      protein: 6.5,
      iron: '45',
    },
    {
      name: 'Donut',
      calories: 452,
      fat: 25,
      carbs: 51,
      protein: 4.9,
      iron: '22',
    },
  ]
  const FakeAPI = {
    async fetch({ page, itemsPerPage, sortBy }) {
      return new Promise(resolve =&gt; {
        setTimeout(() =&gt; {
          const start = (page - 1) * itemsPerPage
          const end = start + itemsPerPage
          const items = desserts.slice()
          if (sortBy.length) {
            const sortKey = sortBy&#091;0].key
            const sortOrder = sortBy&#091;0].order
            items.sort((a, b) =&gt; {
              const aValue = a&#091;sortKey]
              const bValue = b&#091;sortKey]
              return sortOrder === 'desc' ? bValue - aValue : aValue - bValue
            })
          }
          const paginated = items.slice(start, end)
          resolve({ items: paginated, total: items.length })
        }, 500)
      })
    },
  }
  const itemsPerPage = ref(5)
  const headers = ref(&#091;
    {
      title: 'Dessert (100g serving)',
      align: 'start',
      sortable: false,
      key: 'name',
    },
    { title: 'Calories', key: 'calories', align: 'end' },
    { title: 'Fat (g)', key: 'fat', align: 'end' },
    { title: 'Carbs (g)', key: 'carbs', align: 'end' },
    { title: 'Protein (g)', key: 'protein', align: 'end' },
    { title: 'Iron (%)', key: 'iron', align: 'end' },
  ])
  const search = ref('')
  const serverItems = ref(&#091;])
  const loading = ref(true)
  const totalItems = ref(0)
  function loadItems({ page, itemsPerPage, sortBy }) {
    loading.value = true
    FakeAPI.fetch({ page, itemsPerPage, sortBy }).then(({ items, total }) =&gt; {
      serverItems.value = items
      totalItems.value = total
      loading.value = false
    })
  }
&lt;/script&gt;</code></pre>



<p class="wp-block-paragraph">Our application is now ready for our main goal, implement dynamic and custom slots.</p>



<h2 class="wp-block-heading" id="h-what-is-a-custom-slot">What is a custom slot ?</h2>



<p class="wp-block-paragraph">In Vuejs, components can receive props, which can be JavaScript values of any type. But what about template content? Sometimes, we may need to pass a template fragment to a child component, allowing the child component to render this fragment within its own template. And that, is what a slot does.</p>



<h2 class="wp-block-heading" id="h-why-do-we-use-slots">Why do we use slots ?</h2>



<p class="wp-block-paragraph">Slots are a powerful feature in Vue that enable the creation of more flexible and reusable components. Vue slots enable a component to accept dynamic content, referred to as slot content, and display it in a designated spot within the component&#8217;s template, which we call the slot outlet. This designated spot is marked by the HTML element &lt;slot&gt;.</p>



<p class="wp-block-paragraph">The element serves as a placeholder for the content provided by the parent component. This approach addresses the limitation of having a fixed template in a component. It allows users to integrate custom content into a Vue component&#8217;s layout, thereby enhancing its flexibility and reusability. In our case, let&#8217;s see how we can customize our data table.</p>



<h2 class="wp-block-heading" id="h-define-dynamic-slots-in-compdatatable-vue">Define Dynamic Slots in CompDataTable.vue</h2>



<p class="wp-block-paragraph">We&#8217;ll define a dynamic slot template within the <code>CompDataTable.vue</code> component to generate slots for each header defined in <code>dataHeaders</code>.</p>



<pre class="wp-block-code"><code>&lt;template&gt;
  &lt;v-data-table-server
    v-model:items-per-page="itemsPerPage"
    :headers="headers"
    :items="serverItems"
    :items-length="totalItems"
    :loading="loading"
    :search="search"
    item-value="name"
    @update:options="loadItems"
  &gt;
    &lt;template
      v-for="header in headers"
      v-slot:&#091;`item.${header.key}`]="{ item }"
    &gt;
      &lt;slot :name="`item.${header.key}`" :value="item&#091;header.key]"&gt;
        {{ item&#091;header.key] }}
      &lt;/slot&gt;
    &lt;/template&gt;
  &lt;/v-data-table-server&gt;
&lt;/template&gt;</code></pre>



<p class="wp-block-paragraph">Below please find explanation about different parts of the code:</p>



<ul class="wp-block-list">
<li><code>Dynamic Slots:</code> The v-for directive iterates over each header in the headers array. For each header, a slot is created dynamically.</li>



<li><code>Slot Name:</code> The <code>v-slot</code> directive is bound dynamically using square bracket syntax (<code>[]</code>) and a computed key: <code>item.${header.key}</code>. This creates slots dynamically for each column of the data table, allowing for customized rendering of each column&#8217;s content. This creates slots like item.name, item.calories, etc.</li>



<li><code>Slot Props:</code> The { item } syntax destructures the slot props. The <code>item</code> variable represents the data associated with each row of the data table. When using scoped slots in Vue.js, the parent component passes scoped data to the slot content. In this context, <code>item</code> refers to the specific data associated with the current row being rendered in the data table. This allows for customization of each cell in the data table based on the specific data associated with that cell&#8217;s row.<br>In the context of the v-data-table-server component, Vuetify internally provides the item object representing the current row of data being rendered. When defining slots with v-slot, the content inside the slot gets access to the current item, enabling you to use its properties dynamically.</li>
</ul>



<h2 class="wp-block-heading" id="h-using-custom-slots-in-app-vue">Using Custom Slots in App.vue</h2>



<p class="wp-block-paragraph">Next, we’ll use these dynamic slots in <code>App.vue</code> to customize the content for each column. For example, let&#8217;s start by changing the color of our column <code>calories</code>.</p>



<p class="wp-block-paragraph">App.vue</p>



<pre class="wp-block-code"><code>&lt;template&gt;
  &lt;CompDataTable&gt;
    &lt;template v-slot:item.calories="{ value }"&gt;
      &lt;span style="color: red"&gt;{{ value }} kcal&lt;/span&gt;
    &lt;/template&gt;
  &lt;/CompDataTable&gt;
&lt;/template&gt;

&lt;script setup&gt;
  import CompDataTable from './CompDataTable.vue'
&lt;/script&gt;</code></pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="976" height="540" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-37.png" alt="" class="wp-image-33221" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-37.png 976w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-37-300x166.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-37-768x425.png 768w" sizes="auto, (max-width: 976px) 100vw, 976px" /></figure>



<p class="wp-block-paragraph">Here&#8217;s a breakdown of what&#8217;s happening:</p>



<ul class="wp-block-list">
<li><strong><code>v-slot:item.calories</code></strong>: This is a scoped slot directive (<code>v-slot</code>) that targets the slot named <code>item.calories</code>. In the context of a data table, <code>item</code> usually represents a single row of data, and <code>calories</code> likely refers to one of the columns in the data table.</li>



<li><strong><code>{ value }</code></strong>: This is the destructuring syntax used to extract the value associated with the slot. In Vue.js, when passing scoped data to a slot, the parent component can provide scoped data to the slot content. Here, <code>{ value }</code> indicates that the parent component (in this case, <code>CompDataTable</code>) is passing the value associated with the <code>calories</code> column to the slot content. This scoped data allows the slot content to access the specific value of the <code>calories</code> column for each row of the data table.</li>



<li><strong>Slot Content</strong>: Inside the slot content, there&#8217;s a <code>&lt;span&gt;</code> element styled with red color (<code>color: red</code>). The <code>{{ value }} kcal</code> part is where the scoped data is being used. <code>{{ value }}</code> outputs the value of the <code>calories</code> column for the current row, and <code>"kcal"</code> is appended to it. This customization allows for the rendering of the <code>calories</code> column with a specific style and formatting.</li>
</ul>



<p class="wp-block-paragraph">We could also add a custom logo or even an action button. Even both combined, for example a trash button to delete a row from the data table. The possibilities are limitless <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f600.png" alt="😀" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="968" height="527" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-41.png" alt="" class="wp-image-33225" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-41.png 968w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-41-300x163.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/05/image-41-768x418.png 768w" sizes="auto, (max-width: 968px) 100vw, 968px" /></figure>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p class="wp-block-paragraph">Understanding how to dynamically bind slots in Vue.js allows for powerful customization of components. Especially in scenarios like data tables where each column may require unique rendering logic. By using scoped slots and passing scoped data, we can create highly flexible and reusable components that adapt to various data structures and UI requirements. In the provided code snippet, the <code>item</code> variable plays a crucial role in providing the necessary data context for customizing the content of each cell in the data table.</p>



<p class="wp-block-paragraph">I hope this blog will be useful to you and that you now understand how to use dynamic slots in Vue.js with Vuetify data table. Also, don&#8217;t mind sharing your experiences about it <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-use-dynamic-slots-in-vue-js-with-vuetify-data-table/">How to Use Dynamic Slots in Vue.js with Vuetify Data Table</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/how-to-use-dynamic-slots-in-vue-js-with-vuetify-data-table/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: www.dbi-services.com @ 2026-07-26 15:16:05 by W3 Total Cache
-->