2.3. Developing Locally
This post shows how to download the code for the initialized app and set it up for local development.
Download and Extract
In order to customize the initialized app and run it locally, we need to download it from refine.new using the Build and Download
button available in the refine.new dashboard:
We need to extract the zipped folder in to a directory of our choice and then initiate a git repository. If you want to store it on GitHub, please feel free to create a remote repo and push local changes to it.
To deploy the app locally, we run:
npm install
npm run dev
After this, like it does on refine.new, the app should also run locally at http://localhost:5173
.
At this point, we would like to start exploring the code generated by the refine CLI Wizard which worked behind the scenes with refine.new.
refine <App />
Component
We carry out extensive inspections of the generated code throughout this tutorial, but let's first start with the <App />
component. It looks like this:
Show App.tsx code
import { Authenticated, GitHubBanner, Refine } from "@refinedev/core";
import { RefineKbar, RefineKbarProvider } from "@refinedev/kbar";
import {
ErrorComponent,
notificationProvider,
RefineSnackbarProvider,
ThemedLayoutV2,
ThemedTitleV2,
} from "@refinedev/mui";
import { CssBaseline, GlobalStyles } from "@mui/material";
import routerBindings, {
CatchAllNavigate,
NavigateToResource,
UnsavedChangesNotifier,
} from "@refinedev/react-router-v6";
import dataProvider from "@refinedev/simple-rest";
import { useTranslation } from "react-i18next";
import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom";
import { authProvider } from "./authProvider";
import { AppIcon } from "./components/app-icon";
import { Header } from "./components/header";
import { ColorModeContextProvider } from "./contexts/color-mode";
import {
BlogPostCreate,
BlogPostEdit,
BlogPostList,
BlogPostShow,
} from "./pages/blog-posts";
import {
CategoryCreate,
CategoryEdit,
CategoryList,
CategoryShow,
} from "./pages/categories";
import { ForgotPassword } from "./pages/forgotPassword";
import { Login } from "./pages/login";
import { Register } from "./pages/register";
function App() {
const { t, i18n } = useTranslation();
const i18nProvider = {
translate: (key: string, params: object) => t(key, params),
changeLocale: (lang: string) => i18n.changeLanguage(lang),
getLocale: () => i18n.language,
};
return (
<BrowserRouter>
<GitHubBanner />
<RefineKbarProvider>
<ColorModeContextProvider>
<CssBaseline />
<GlobalStyles
styles={{ html: { WebkitFontSmoothing: "auto" } }}
/>
<RefineSnackbarProvider>
<Refine
dataProvider={dataProvider(
"https://api.fake-rest.refine.dev"
)}
notificationProvider={notificationProvider}
authProvider={authProvider}
i18nProvider={i18nProvider}
routerProvider={routerBindings}
resources={[
{
name: "blog_posts",
list: "/blog-posts",
create: "/blog-posts/create",
edit: "/blog-posts/edit/:id",
show: "/blog-posts/show/:id",
meta: {
canDelete: true,
},
},
{
name: "categories",
list: "/categories",
create: "/categories/create",
edit: "/categories/edit/:id",
show: "/categories/show/:id",
meta: {
canDelete: true,
},
},
]}
options={{
syncWithLocation: true,
warnWhenUnsavedChanges: true,
}}
>
<Routes>
<Route
element={
<Authenticated
fallback={
<CatchAllNavigate to="/login" />
}
>
<ThemedLayoutV2
Header={() => (
<Header isSticky={true} />
)}
Title={({ collapsed }) => (
<ThemedTitleV2
collapsed={collapsed}
text="refine Project"
icon={<AppIcon />}
/>
)}
>
<Outlet />
</ThemedLayoutV2>
</Authenticated>
}
>
<Route
index
element={
<NavigateToResource resource="blog_posts" />
}
/>
<Route path="/blog-posts">
<Route
index
element={<BlogPostList />}
/>
<Route
path="create"
element={<BlogPostCreate />}
/>
<Route
path="edit/:id"
element={<BlogPostEdit />}
/>
<Route
path="show/:id"
element={<BlogPostShow />}
/>
</Route>
<Route path="/categories">
<Route
index
element={<CategoryList />}
/>
<Route
path="create"
element={<CategoryCreate />}
/>
<Route
path="edit/:id"
element={<CategoryEdit />}
/>
<Route
path="show/:id"
element={<CategoryShow />}
/>
</Route>
<Route
path="*"
element={<ErrorComponent />}
/>
</Route>
<Route
element={
<Authenticated fallback={<Outlet />}>
<NavigateToResource />
</Authenticated>
}
>
<Route path="/login" element={<Login />} />
<Route
path="/register"
element={<Register />}
/>
<Route
path="/forgot-password"
element={<ForgotPassword />}
/>
</Route>
</Routes>
<RefineKbar />
<UnsavedChangesNotifier />
</Refine>
</RefineSnackbarProvider>
</ColorModeContextProvider>
</RefineKbarProvider>
</BrowserRouter>
);
}
export default App;
As we can notice, there are so many things going on here. So, in the coming units, we break it down and explain part by part the ones important to us. Below, we discuss the <Refine />
component before we move to the next post.
The <Refine />
Component
The <Refine />
component is the centerstage of a refine app. It is where all the main action in our React admin panel app happens. It is a wrapper component that hosts the contexts of all the app's components and are supplied their provider configurations.
As we can see, it is crowded with many props that basically represent configurations for different layers of the app, like data fetching to and from APIs, authentication and authorization, resource defintions, routing, etc.
The <Refine />
component is used to configure top-level settings of the application. Most of <Refine />
's props are configured as provider objects, but there are some others that are not backed by contexts, such as the resources
and options
.
This tutorial covers the dataProvider
, authProvider
and resources
props which are enough to guide us to more advanced stuff:
<Refine
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
authProvider={authProvider}
resources={[
{
name: "blog_posts",
list: "/blog-posts",
create: "/blog-posts/create",
edit: "/blog-posts/edit/:id",
show: "/blog-posts/show/:id",
meta: {
canDelete: true,
},
},
{
name: "categories",
list: "/categories",
create: "/categories/create",
edit: "/categories/edit/:id",
show: "/categories/show/:id",
meta: {
canDelete: true,
},
},
]}
// other props
>
We delve into these props starting Unit 3 with dataProvider
. However, refine has a very powerful tool, the Inferencer, which automatically coded all the CRUD pages we have already in use in our admin panel app. We, therefore, spare the next post to preview the pages generated by Inferencer.