Front-End Assets
Introductionβ
Bow comes preconfigured with a modern Frontend environment based on Vite, including:
- React 19
- Vue.js 3
- Tailwind CSS 3
- Sass/SCSS
This setup provides instant reloading (HMR) and ultra-fast compilation.
Installationβ
npm install
Commandsβ
| Command | Description |
|---|---|
npm run dev | Starts the Vite development server with HMR |
npm run build | Compiles and optimizes for production |
Developmentβ
npm run dev
Starts the Vite server with hot reloading. Changes are reflected instantly without reloading the page.
Productionβ
npm run build
Compiles and minifies the assets into the public/ folder.
File Structureβ
assets/
βββ js/
β βββ app.js # Main entry point
β βββ Example.jsx # React component
β βββ Example.vue # Vue component
βββ sass/
β βββ app.scss # Main styles
β βββ variables.scss # SCSS variables
βββ css/
βββ app.css # CSS/Tailwind
Vite Configurationβ
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import react from '@vitejs/plugin-react';
import path from 'path';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
vue(),
react(),
tailwindcss(),
],
root: path.resolve(__dirname, 'assets'),
build: {
outDir: path.resolve(__dirname, 'public'),
emptyOutDir: true,
rollupOptions: {
input: {
app: path.resolve(__dirname, 'assets/js/app.js'),
},
output: {
entryFileNames: 'js/[name].js',
assetFileNames: (assetInfo) => {
if (/\.(css|scss)$/.test(assetInfo.name)) {
return 'css/[name]-[hash][extname]';
}
if (/\.(png|jpe?g|gif|svg|webp)$/.test(assetInfo.name)) {
return 'img/[name]-[hash][extname]';
}
return '[ext]/[name]-[hash][extname]';
},
},
},
},
});
Tailwind CSSβ
Configurationβ
import defaultTheme from "tailwindcss/defaultTheme";
export default {
content: [
"./templates/**/*.{blade,tintin}.php",
"./templates/**/*.twig",
"./templates/**/*.{js,jsx,ts,tsx,vue}",
"./assets/js/**/*.{js,jsx,ts,tsx,vue}",
],
theme: {
extend: {
fontFamily: {
sans: ["Figtree", ...defaultTheme.fontFamily.sans],
},
},
},
plugins: [],
};
Usageβ
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom styles */
.btn-primary {
@apply bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600;
}
React Componentsβ
import React, { useState } from 'react';
export default function Example() {
const [count, setCount] = useState(0);
return (
<div className="p-4">
<h1 className="text-2xl font-bold">Compteur: {count}</h1>
<button
className="btn-primary mt-4"
onClick={() => setCount(count + 1)}
>
IncrΓ©menter
</button>
</div>
);
}
Vue Componentsβ
<script setup>
import { ref } from 'vue';
const count = ref(0);
</script>
<template>
<div class="p-4">
<h1 class="text-2xl font-bold">Compteur: {{ count }}</h1>
<button class="btn-primary mt-4" @click="count++">
IncrΓ©menter
</button>
</div>
</template>
Template Integrationβ
With Tintinβ
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
<div id="app"></div>
<script type="module" src="/js/app.js"></script>
</body>
</html>
The asset helperβ
The asset() helper simply builds a URL to a static file in the public/
folder. It is not aware of the cache hash added by Vite in production β reserve
it for files whose names do not change (favicon, static images, etc.).
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
<script src="{{ asset('js/app.js') }}"></script>
The vite helper (compiled assets)β
In production, npm run build adds a hash to each file name
(app-CBsLkjX9.js) for cache-busting, and records the mapping from logical
name to actual file in a manifest. The vite() helper reads this manifest
and returns the URL of the corresponding compiled file:
<link rel="stylesheet" href="{{ vite('css/app.css') }}">
<script type="module" src="{{ vite('js/app.js') }}"></script>
Signature:
vite(string $file, bool $absolute = false): string
$file: the entry key in the manifest (e.g.js/app.js). Each referenced file must be declared as an entry (input) invite.config.jsin order to appear in the manifest.$absolute: iftrue, the URL is prefixed with the application URL (viaurl()) instead of being relative.- Throws an
Exceptionif the entry does not exist in the manifest.
The helper looks for the manifest, in order, at the following locations:
public/build/.vite/manifest.json
public/build/manifest.json
public/.vite/manifest.json
manifest.json (project root)
vite() returns URLs prefixed with /build/ (e.g.
/build/js/app-CBsLkjX9.js). The compiled files must therefore be served
under /build/: make sure the build output (outDir in vite.config.js) and
the manifest location are consistent with this prefix.
During npm run dev, assets are served by the Vite server with HMR. The
vite() helper is only useful after a npm run build, when the manifest
exists. Remember to run the build before testing in production mode.
Environment Variablesβ
Vite exposes variables prefixed with VITE_:
{
"VITE_APP_NAME": "Mon App",
"VITE_API_URL": "https://api.example.com"
}
// Access in the code
const appName = import.meta.env.VITE_APP_NAME;
For more information, see the Vite documentation.
Is something missing?
If you run into problems with the documentation or have suggestions to improve the documentation or the project in general, please open an issue for us, or send a tweet mentioning the Twitter account @bowframework or directly on github.