| 1 | import { Button } from 'vocs/components' |
| 2 | |
| 3 | # `client` |
| 4 | |
| 5 | bhvr uses Vite + React as its default client side template, as React has become one of the industry defaults with a vast amount of ecosystem support. With that said you can absolutely replace it with your client of choice by [following the guide](). We highly recommend using [Vite](https://vite.dev) as your bundler since it too has lots of ecosystem suppport and is very lightweight. |
| 6 | |
| 7 | |
| 8 | ## Basics |
| 9 | |
| 10 | Just in case you haven't used Vite + React much here are some basics you may want to keep in mind. |
| 11 | |
| 12 | ### Client Side Only |
| 13 | |
| 14 | Unlike Next.js, Vite + React is client side only. This means any kind of environment variable used here will be publicly accessible, which is why we have a [`server`](/packages/server) package to keep those secure. This can take some getting used to when building an application and how you might fetch data. A classic way to achieve an "on-load" API call to your server is through a `useEffect`. |
| 15 | |
| 16 | ```tsx App.tsx |
| 17 | import { useState, useEffect } from 'react'; |
| 18 | |
| 19 | function UserProfile() { |
| 20 | const [user, setUser] = useState(null); |
| 21 | const [loading, setLoading] = useState(true); |
| 22 | const [error, setError] = useState(null); |
| 23 | |
| 24 | useEffect(() => { |
| 25 | // Define an async function inside useEffect |
| 26 | async function fetchUser() { |
| 27 | try { |
| 28 | setLoading(true); |
| 29 | // Make the API call to your server |
| 30 | const response = await fetch(`${import.meta.env.SERVER_URL}`); |
| 31 | |
| 32 | // Handle non-200 responses |
| 33 | if (!response.ok) { |
| 34 | throw new Error(`Error: ${response.status}`); |
| 35 | } |
| 36 | |
| 37 | const data = await response.json(); |
| 38 | setUser(data); |
| 39 | setError(null); |
| 40 | } catch (err) { |
| 41 | setError(err.message); |
| 42 | setUser(null); |
| 43 | } finally { |
| 44 | setLoading(false); |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | // Call the function |
| 49 | fetchUser(); |
| 50 | |
| 51 | // If needed, you can return a cleanup function |
| 52 | return () => { |
| 53 | // Any cleanup code (if needed) |
| 54 | }; |
| 55 | }, []); // Empty dependency array means this runs once on mount |
| 56 | |
| 57 | if (loading) return <div>Loading...</div>; |
| 58 | if (error) return <div>Error: {error}</div>; |
| 59 | if (!user) return <div>No user data found</div>; |
| 60 | |
| 61 | return ( |
| 62 | <div> |
| 63 | <h2>User Profile</h2> |
| 64 | <p>Name: {user.name}</p> |
| 65 | <p>Email: {user.email}</p> |
| 66 | </div> |
| 67 | ); |
| 68 | } |
| 69 | ``` |
| 70 | |
| 71 | ### Environment Variables |
| 72 | |
| 73 | Despite environment variables being public in this setup, they still come in handy for things like working with your local server URL vs your deployed instance. It's best practice to keep your variables in a `.env.local` file with the following format, taking special note that they need to start with `VITE_`. |
| 74 | |
| 75 | ``` |
| 76 | // [!code word:VITE] |
| 77 | VITE_MY_VAR=value |
| 78 | ``` |
| 79 | |
| 80 | To use them inside your app be sure to use this Vite formatting |
| 81 | |
| 82 | ```typescript |
| 83 | const variable = import.meta.env.VITE_MY_VAR |
| 84 | ``` |
| 85 | |
| 86 | ### Check the Config |
| 87 | |
| 88 | Vite's config file `vite.config.ts` is worth exploring as it can provide some extra options and plugins. |
| 89 | |
| 90 | ## Styles |
| 91 | |
| 92 | When creating a new bhvr project you can use the CLI to specify the CSS template you want |
| 93 | |
| 94 | ```bash [terminal] |
| 95 | bun create bhvr@latest --template default # Classic CSS |
| 96 | bun create bhvr@latest --template tailwind # Tailwind installed and setup |
| 97 | bun create bhvr@latest --template shadcn # Tailwind + Shadcn/ui component setup |
| 98 | ``` |
| 99 | |
| 100 | ## Routing |
| 101 | |
| 102 | There are serveral ways to handle routing in your client app, but few come close to [React Router](http://reactrouter.com). Setting it up is quite simple and intuitive. |
| 103 | |
| 104 | ::::steps |
| 105 | |
| 106 | ### Install `react-router` |
| 107 | |
| 108 | Make sure you're inside the `client` directory and then install `react-router` |
| 109 | |
| 110 | ```bash [terminal] |
| 111 | bun add react-router |
| 112 | ``` |
| 113 | |
| 114 | ### Setup Router |
| 115 | |
| 116 | You can do this inside `main.tsx` or `App.tsx`, I prefer the latter. All you have to do is import the `BrowserRouter`, `Routes`, then declare your routes with the components they go to inside. |
| 117 | |
| 118 | ```tsx App.tsx |
| 119 | import { BrowserRouter, Routes, Route } from "react-router"; |
| 120 | import Home from "./components/Home"; |
| 121 | |
| 122 | function App() { |
| 123 | return ( |
| 124 | <BrowserRouter> |
| 125 | <Routes> |
| 126 | <Route path="/" element={<Home />} /> |
| 127 | </Routes> |
| 128 | </BrowserRouter> |
| 129 | ); |
| 130 | } |
| 131 | |
| 132 | export default App |
| 133 | ``` |
| 134 | |
| 135 | ### Use Dynamic Routes |
| 136 | |
| 137 | If you want to have a dynamic route with a path param you can set it up in your `Routes` like so |
| 138 | |
| 139 | ```tsx |
| 140 | import { BrowserRouter, Routes, Route } from "react-router"; |
| 141 | import Home from "./components/Home"; |
| 142 | import Post from "./components/Post"; |
| 143 | |
| 144 | function App() { |
| 145 | return ( |
| 146 | <BrowserRouter> |
| 147 | <Routes> |
| 148 | <Route path="/" element={<Home />} /> |
| 149 | <Route path="/post/:slug" element={<Post />} /> |
| 150 | </Routes> |
| 151 | </BrowserRouter> |
| 152 | ); |
| 153 | } |
| 154 | |
| 155 | export default App |
| 156 | ``` |
| 157 | |
| 158 | Then inside the component you can access those params with `useParams` |
| 159 | |
| 160 | ```typescript |
| 161 | import { useParams } from "react-router"; |
| 162 | |
| 163 | function Post(){ |
| 164 | const { slug } = useParams() |
| 165 | |
| 166 | return ( |
| 167 | <> |
| 168 | <h1>Post {slug}</h1> |
| 169 | </> |
| 170 | ) |
| 171 | } |
| 172 | |
| 173 | export default Post; |
| 174 | ``` |
| 175 | |
| 176 | :::: |
| 177 | |
| 178 | ## Deployment |
| 179 | |
| 180 | <Button href="/deployment/client/orbiter">Deployments Section</Button> |