Hi guys !
J'espère que vous avez tous vu les nouveaux kits de démarrage de Laravel avec Inertia, React et Shadcn/UI. C'est beau...compliqué mais beau.
Suite à mon précédent article React dans Symfony avec Vite je me suis demandé pour les besoin d'un projet comment faire un front en React avec Shadcn/UI et un backend Symfony.
Quelques précisions : ici on va utiliser une structure "Fullstack" (tout dans le même projet) alors qu'on pourrait coder un front en React isolé et juste le backend en Symfony. Pour des raisons de simplicité, notamment pour la gestion de l'authentification, c'est beaucoup plus simple de faire comme ça. Mais au final, notre front et notre back vont bien communiquer via des API REST comme si ils étaient isolés.
Allez c'est parti !
Création du projet et ajout de React
Je ne vais pas refaire le tuto. Créez votre projet comme indiqué dans mon précédent tuto React dans Symfony avec Vite
Vérifiez bien que React marche.
Installation de Tailwind
On va installer Tailwind et son plugin Vite.
A la racine de votre projet :
npm install tailwindcss @tailwindcss/vite
Puis ouvrez le fichier vite.config.js et configurez-le comme suit:
import { defineConfig } from "vite";
import symfonyPlugin from "vite-plugin-symfony";
import react from '@vitejs/plugin-react';
import path from "path"
import tailwindcss from "@tailwindcss/vite"
export default defineConfig({
plugins: [
react(),
tailwindcss(),
symfonyPlugin(),
],
build: {
rollupOptions: {
input: {
app: "./assets/main.jsx"
},
}
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./assets"),
},
}
});
On a ajouté :
- L'import du plugin Tailwind ligne 5
- le plugin dans la liste des plugins
- On a ajouté l'alias '@' qu'on définit à la racine de notre répertoire assets (important pour la suite sinon Shadcn ne sait pas où mettre ses composants)
Puis ajoutez la ligne suivante dans assets/styles/app.css:
@import "tailwindcss";
Ajoutez 2/ 3 classes dans votre App.jsx pour vérifier que ça marche bien après avoir relancé un "npm run dev"
Installation de Shadcn/UI
On installe tous les pré-requis:
npm install class-variance-authority clsx tailwind-merge lucide-react tw-animate-css
Et là... la doc n'est pas claire et il y a une incohérence.
Celle-ci ne fait mention que du fichier tsconfig.json, or celui-ci appelle 2 autres fichiers tsconfig.app.json et tsconfig.node.json (faut lire hein, c'est marqué dedans !).
Note : Pour aller récupérer les 2 fichiers absents de la documentation, j'ai créé un projet Vite bidon (avec npm create vite@latest my-project)
Créons maintenant nos 3 fichiers à la racine de notre projet.
tsconfig.json
{
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.node.json"
}
],
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./assets/*"]
}
}
}
tsconfig.app.json
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["assets"]
}
tsconfig.node.json
{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"moduleDetection": "force",
"noEmit": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true
},
"include": ["vite.config.ts"]
}
Note : je n'ai rien modifié (simplement copié du projet Vite exemple) sauf la dernière ligne "include": ["assets"] de tsconfig.app.json que j'ai modifié de 'src' à 'assets' pour coller à nos répertoires Symfony.
Mais c'est pas fini !
On va ajouter les styles Shadcn à notre CSS.
Ouvrez le fichier assets/styles/app.css et ajouter les lignes comme suit (nous ne devriez avoir que la première ligne déjà présente):
@import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *));
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.145 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.145 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.985 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.396 0.141 25.723);
--destructive-foreground: oklch(0.637 0.237 25.331);
--border: oklch(0.269 0 0);
--input: oklch(0.269 0 0);
--ring: oklch(0.439 0 0);
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(0.269 0 0);
--sidebar-ring: oklch(0.439 0 0);
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
Créez un fichier assets/lib/utils.ts tel que:
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Et enfin un dernier fichier components.json à la racine du projet:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "",
"css": "assets/styles/app.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
Normalement c'est tout en terme de configuration.
On va faire un test dans App.jsx pour voir si on peut ajouter un composant Alert de ShadCN.
Installation du composant Alert:
npx shadcn@latest add alert
Puis, modifiez votre App.jsx tel que:
import { useState } from 'react'
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { Terminal } from "lucide-react";
export default function App() {
const [count, setCount] = useState(0)
return (
<div className="relative isolate overflow-hidden bg-white px-6 py-24 sm:py-32 lg:overflow-visible lg:px-6">
<h1 className="mb-3">Vite + React + Shadcn</h1>
<Alert variant="default | destructive">
<Terminal/>
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>
You can add components and dependencies to your app using the cli.
</AlertDescription>
</Alert>
</div>
)
}
Et voilà !
Notez qu'à chaque modification de votre code, Vite recompile et met à jour la page dans votre navigateur.
Amusez-vous bien !
Top comments (0)