Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

âš¡ Complete Tutorial: React Admin Panel with refine and daisyUI

Posted on Oct 24 • Originally published at refine.dev Author: Abdullah NumanIn this post, we go through the process of developing a React admin panel using refine and daisyUI.refineis a React-based framework that helps quickly build data-heavy applications like dashboards, admin panels and storefronts. It comes with a headless core package that integrates with any UI framework and design system.daisyUI is a Component templates library built on top of TailwindCSS. It provides us with short semantic classes composed from TailwindCSS utilities and a growing collection of convenient component templates that helps quickly build React components for our app.daisyUI can easily be integrated with refine, and in this post we are going to see how to do that while building a dashboard and admin panel app using refine's Fine Foods API.You can get the complete source code of this tutorial app from here => https://github.com/refinedev/refine/tree/master/examples/blog-refine-daisyuiThe React admin panel we are going to build uses the refine hosted Fine Foods API to display a dashboard of KPI data, and CRUD pages for products and categories resources. The dashboard will present various data in cards, charts and a table. And the products and categories resources will have list, create, show and edit pages.We start this post with a brief discussion on refine architecture - particularly how it works under the hood with React contexts backed by providers, hooks and components. We also talk about daisyUI, the short, semantic classes such as btn, menu, tab, etc., and their variants it provides and how they facilitate rapid building of React components using a growing library of prestyled daisyUI templates.We then initialize a refine app, and integrate and configure daisyUI. Afterwards, we move on to building the features of the admin panel.We first build the dashboard page where we present stats for relevant KPIs in cards, charts and a table. We use the React-based Recharts library for plotting our data.In the later half of the post, we add CRUD pages for products and categories resources. We define the resources prop on component, resource action paths, and their route defintions. CRUD actions covered for both resources are list, create, show, update and delete. We then make use of refine hooks such as useTable() and useForm() for entering, fetching and presenting data from the API. We build the UI with predefined daisyUI templates for buttons, menus, tabs, stats, etc.Towards the end, we see how to customize the layout of a refine app. We replace the default leftside navigation to adopt a top navbar by leveraging useMenu(), useNavigation() and useBreadcrumb() hooks.refine is a powerful React framework for building Enterprise web applications. It is particularly focused on creating data-heavy apps like dashboards, admin panels and internal tools. It comes with a core headless package that provides different sets of hooks and components for dealing with concerns like data fetching, authentication, authorization, etc. It also has supplmentary packages which enable rapid development of React applications by integrating with various backend services like Airtable, Supabase and Strapi as well as UI frameworks like Ant Design, Material UI, Chakra UI and Mantine.refine separates app concerns such as data fetching, authentication, access control, etc., into layers of React contexts each backed by a provider object, a set of corresponding hooks as well as relevant components. For example, the data layer represents a context dependent on a dataProvider object with a set of methods for handling CRUD actions. The data layer is accessed with a set of data hooks that help invoke the CRUD methods from UI components.This means, we would have all CRUD related methods such as getList(), create(), show(), update() and delete() inside a dataProvider object and we are able to access them from a UI component using useList(), useCreate(), etc., data hooks. The data hooks, in turn, make use of React Query for data fetching, caching state management and error handling.The refine data hooks mentioned above are basically core hooks. Higher level hooks which are built top of these hooks exist, such as the useTable() hook provided by @refinedev/react-table support package that integrates React Table with refine core. Higher level hooks adds additional features that increase development efficiency. For example, the useList() hook is employed by the useTable() hook that helps present data in a table using all the features of React Table. Similarly, the useCreate() core data hook is utilized inside the useForm() high level hook provided by the @refinedev/react-hook-form package which augments form related CRUD actions with React Hook Form.refine's resource definitions are specified inside the resources object. The resources object is passed to the resources prop of the component. Resource definitions, in combination with route definitions, set up a refine app's nav menu items, their navigation URLs, as well as breadcrumbs, and help infer the default resource name of a CRUD page along a route.Routing in refine is supported by the react-router-dom package. refinev4 supports explicit routing by delegating everything related to routing to the React Router APIs.refine's Inferencer is a powerful tool for quickly scaffolding CRUD pages and automatically generating code for a resource page. The Inferencer works by first polling a particular API endpoint to get the shape of the data and then placing all the hooks and UI elements necessary to fetch and present the data on a page.refine's core package is designed to be "headless" which gives the freedom to integrate it with any UI component library or framework.daisyUI is an open source UI library built on top of TailwindCSS. It provides short, component-oriented, semantic classes composed from regular, longer Tailwind class strings that typically contribute to clumsy markup in an application. daisyUI hosts a growing collection of pre-styled templates for components like buttons, menus, and tabs with responsive, size, shape, and color variants.Composing responsive, color, size, and shape variant classes manually with the @apply directive is practically inefficient with plain TailwindCSS classes. daisyUI does this out-of-the-box. On top of that, these component styles can be overridden or extended with the usual TailwindCSS utilities. As a result, daisyUI offers the convenience of using smaller class names, a smaller CSS file size, configurable number of variants, and greater customization without compromising much of the code quality.Feel free to check out the daisyUI documentation to learn more.For this app, we are going to start with refine's headless core, using create refine-app to scaffold our pages and generate the initial page code. We will then make necessary logic and UI adjustments and then apply daisyUI classes to our components.So, let's get started with initialzing the refine app first.We'll create a local repository by using the create refine-app CLI-based app scaffolder. Run the following npm command from the directory of your choice to interactively initialize the project.Select the following options when prompted:Take a note of the Headless choice. We are asking for refine core package with plain JSX markup.After completing the app initialization, let's navigate to the project folder and start our app with:We should be greeted with the app's Welcome page.We'll replace the Fake REST API with Fine Foods URL in the dataProvider prop. Update the App.tsx file to the following:With these changes, we'll start fresh towards building the dashboard page first and then later move on to the CRUD pages for products and categories resources. At this point, we don't have any resources or their pages.Notice, we are now using the Fine Foods REST API in the dataProvider prop of .The Fine Foods API is an example of the REST API hosted by refine with a collection of end points. In this app, we will be querying the /dailyRevenue, /dailyOrders, /newCustomers and /orders endpoints for fetching data for our dashboard page. Later on, we'll also be accessing its /products and /catgories endpoints for our resource pages.We are using daisyUI as our UI library. In order to integrate daisyUI into our refine app, we have to first perform a Vite installation of TailwindCSS, its dependencies, and set up their configurations.Go ahead an follow the below steps to first add TailwindCSS, PostCSS and Autoprefixer packages and then initialize tailwind.config.js:Install TailwindCSS and related packagesWe'll be using the custom classes in this App.css, so feel free to copy it over.If you need a hand with TailwindCSS installation, please follow this guide for installing TailwindCSS with ViteInstall and setup daisyUIWith TailwindCSS set up properly, it's now turn to install and configure daisyUI.More details on customizing a daisyUI theme is available on the docs hereAfter these changes, with the server running, TailwindCSS watches for the use of daisyUI and TailwindCSS classes, and automatically compiles updated styles.We have to install refine's support packages for React Table and React Hook Form. We are using Tailwind Heroicons for our icons, the Day.js library for time calculations and Recharts library to plot our charts for KPI data. So, run the following and we are good to go:We'll be using the following interfaces throughout the app. So, feel free to copy and paste them over to src/interfaces/index.ts or a similar location.Now that we have all the set up and packages ready, it's time for us to start building the dashboard page. The dashboard page will be displayed at the index route and contain KPI stats, charts and a table of data.We'll add a /dashboard directory under src/pages and add the component to the index.tsx file under it.Initially, it will return a dummy hero component. We'll update it in the coming sections.We'll display the dashboard page at the /dashboard path and make it the index route. So, let's add the necessary resource and routes to the component in App.tsx.Update the App.tsx as below:We have updated our imports and passed the resources prop to . We have defined a dashboard resource with only one page: the list. In the route definitions, as children to , we have assigned the page to the /dashboard route and set it as the index route.With these additions and changes, when we navigate to / or /dashboard, we should be able to see the dashboard page. It looks somewhat dashing like this:Let's now focus on implementing the features of the dashboard. Inside it, we'll have a component that takes in KPI data and returns a component for each. We'll create the component inside src/components/dashboard. And use the following code:The relays and displays KPI data inside the component, so let's work on that now.Let's create the component inside src/components/dashboard directory. The represents an individual stat item. It takes in a number of props and displays the KPI data with an icon. It looks like this:Note that we have started using daisyUI classes in the component. stat, stat-figure, stat-title, stat-value, stat-desc are classes provided by the daisyUI Stats template.With the and components completed, we are ready to update the component. Inside it, we will query a number of Fine Foods end points to gather revenue, orders and customers data and then transform them. We will pass the transformed data to child components, one by one as they get built.For now, we'll import and display the component and pass it three sets of KPI data in order to display the KPI cards at the top of the dashboard page.Replace the code inside the component with the following:Notice we are fetching data from three Fine Foods API end points: /dailyRevenue, /dailyOrders and /newCustomers. We are fetching them with the useList() refine core hook. We are querying them as resources although in our refine admin panel app they are not. The filters object is used to get the past 7 days' data.You can find more details in the refine useList() docs here.With these changes, our dashboard page has three KPI cards displayed at the top:Now we want to display three charts inside a tabs panel below the KPI cards. So, we'll create the , and components inside the src/components/dashboard directory. We'll also create two chart components, a and a to plot KPI data. Once all components are ready, we'll add the component to .The component will house the other two tab components, so in order to avoid linter and browser errors, we'll start with the children.We need to have the button for accessing a tab panel. So, create the component as below:The will contain a chart which can be accessed by clicking on a . Let's go ahead and create the component with the following code:The component will contain the tab view logic and state. It will accept the tabs object and display a and as children for each item in the tabs object. Let's create the component with the code below:With the tab components ready, we need to create three charts to be displayed inside by mapping through the tabs object. We want to implement them using Recharts. One and one .For plotting the data, will use the APIs of the Recharts library and will use the APIs. They both will use for responsiveness.You can find all the detials in the Rechats , and documentations if you need to.We'll build the charts inside the src/components/dashboard directory.Create the component with the code below:Inside we are receiving the data along with other props and relaying it to with its data={data} prop. We are using , for ticks and axes labels. We are drawing the area and monotonic line with component and its props. draws a grid in the background. We also have a custom tooltip shown inside .In a similar way, create the component with the below code: does the same thing as , except that it draws a bar chart with Recharts component.The charts above use a custom tooltip, the component, to show data point details according to the mouse position.Let's create the component with the following code:Now we have all the components for displaying ready. So, we'll import and display it inside the dashboard below . Let's update the component with the below code:Notice we are defining a useMemoizedChartData() hook to transform the fetched data and memoize them to make them chart ready. We are then setting the tabs object from the data, the charts, and other properties. We eventually pass the tabs object to the component.With these changes, our dashboard page displays a panel of charts accessible from a top tabbed menu:Lastly, we want to display recent sales data in a table below the charts. We have the component that lists recent orders in a table with filtering and sorting features. Let's create the component with the following code:In the component, we are using a useTable() hook, which is a high level hook provided by refine's React Table supported @refinedev/react-table package. It queries the /orders endpoint and implements a table with filtering and sorting features.We'll come to the details of useTable() when we create list pages for products and categories resources.With the component ready, let's import it and display it inside . Update it with the following code:With all these updates, we have completed implementing the dashboard page. It now looks like this:Having completed the dashboard page above, in this section, we'll add CRUD pages for products and categories resources. We'll start implementing the pages for the products resource first.We want list, create, edit and show pages for the products. Since we are using refine's headless core without any supported UI library, it helps if we use the Inferencer to generate the pages for us. We'll leverage the power of refine's component in the CRUD pages.There are two steps to getting the Inferencer generated page code:Scaffold the CRUD pages by running the Inferencer to implement all the products pages with . then generates the actual codes for us that we can get from the page in the browser.Navigate along the /products path to an action route in your browser and get the code from the page by clicking on the Show the auto-generated code button. It is graciously provided to us by refine 😄We'll scaffold the pages first with the following Inferencer command:This produces , , and pages. You can find them under a pluralized /products directory inside src/pages.This also automatically generates the resource definition for products and adds it to the resources array in App.tsx. Resource paths for list, create, show and edit are specified:We have to manually add the route definitions for each action individually to the App.tsx file. So, the updated App.tsx should have the following changes with resources and routes definitions added for products:Notice towards the top that we have imported the scaffolded components (the ones with component, not yet the actual page code). Towards the end, we assigned them to /products paths for defining the routes. Routine React Router DOM stuff.With the above changes, we have added possible actions and their routes for the products resource. We defined the routes and pages for list, create, edit and show actions and have enabled delete action as well. The page mapping for each route are handled with the component.refine maps resource paths to page components via route definitions, and using the map infers the resource name of a page at the current URL of the browser. That way, hooks like useTable() and useNavigation(), and Inferencer components like are always able to infer the default resource name from inside a resource page.You can find more information about resources and routing on the refine documentation.Now when we navigate along the /products paths, we can see some clumsy looking pages in need of proper styling. So, we're interested in getting their code and modifying them according to our needs. We are going to do that one by one in the following sections.To begin with, the scaffolded component looks like this:The infers the resource name and action path from the resource and routes definitions based on the current URL of the browser. It then polls the Fine Foods /products end point to figure out the data shape, and based on the shape, it uses the necessary refine-React Table APIs and JSX markup to present the fetched data in a table.We'll grab the generated code from the page modal by clicking on the Show the auto-generated code button. It is pretty diligent and should look something like this:The generated code implements a handful of features, including data fetching, button actions, pagination, and JSX markup with minimal styles for presenting the data in a table. This is pretty much the skeleton of what we want in a table of data that we want to improve with daisyUI.It uses the useTable() hook provided by @refinedev/react-table package, which augments refine's useTable() core hook with React Table's useReactTable() hook. More on this below.We want to keep most of it and add filter functionality at the top, modify the pagination and apply daisyUI classes for tables, buttons, and groups.So, we'll build on top of it and make necessary logic, markup and style modifications. Replacing the old ones, we'll eventually adopt the following code:It is definitely possible to refactor the components into smaller, testable ones. However, for the purpose of the tutorial, we'll try to keep things on the same page as much as possible. As done below, this will give us the scope to rather focus on explaining the code easily, part by part.1. Data Fetching and ProcessingThe useTable() hook from the @refinedev/react-table package is used to fetch data from the Fine Foods /products endpoint. The refine-React Table's useTable() hook is a higher level hook built on top of refine's core useTable() hook provided by @refinedev/core. It combines the power of useTable() core hook with React Table's useReactTable() APIs:Notice that we are passing React Table column definitions, the columns object, to useTable() hook and grabbing all necessary table props. We are destructuring the table related data with the getRowModel and getHeaderGroups methods and presenting them inside the table.We are making use of pagination props such as setPageIndex, getPageCount, and previousPage returned by useReactTable() to build the client side pagination strip.Filtering utilities such as filters, setCurrent, setFilters are accessed from the refineCore object returned from the query. Inside the JSX, we are using them to build the filter by keywords feature.Notice, we don't need to specify the resource argument to useTable(). It is already inferred from the current URL thanks to the resource and routes definitions in App.tsx.2. Data PresentationIn the table, we are populating the column headers by looping through the getHeaderGroups() array and presenting row data inside the table by doing the same with the array returned from getRowModel().rows.Inside the React Table columns definition object, using the cell property we are customizing the contents of the Actions column to add show, edit and delete buttons:Notice also, we are using the useNavigation() core hook to pick the show() and edit() methods and use them inside the buttons to navigate to their respective routes:Near the top, we're using the create() method inside the Create button that leads to the page.3. daisyUI StylesThroughout the component, we are using short daisyUI classes that come in handy for styling our components. We are using btn and its derived classes such as btn-sm, btn-primary, btn-ghost, btn-circle and btn-outline to style our buttons. For the table, we are using table and table-zebra. For grouping elements, we are using join and join-items.Notice throughout the markup that we are able to seamlessly apply regular Tailwind Flex, responsive and spacing classes as well. We can also use them to customize the styles of the elements that are already using the component classes.With these changes, when we navigate to the /products route, our products list page looks like below:We have already scaffolded the component using the Inferencer. Following the same process described for , we can get the code for component from the page at /products/create. We won't get into the detailed steps here, as moving ahead, you can repeat the process of getting the page code from the modal at all action routes. You can then make the necessary adjustments and come up with the final code.The uses refine's @refinedev/react-hook-form APIs to build forms for create and edit pages. The refine-React Hook Form package integrates the useForm() core hook with the features of React Hook Form's useForm() hook.The modified component looks like below, so replace the code inside src/pages/products/create.tsx with this one:Here's the break down of the component:1. Data Fetching and Form ManagementThe most significant part of the product create page lies in the use of the useForm() hook imported from @refinedev/react-hook-form supplementary package. The refine-React Hook Form useForm() hook combines the power of the useForm() refine core hook that primarily handles form submission, data fetching, caching, state management and serverside error handling. Integrating react-hook-form augments form features to include better form fields state management and error handling.We are grabbing the onFinish object returned from the refine core and passing it to React Hook Form's handleSubmit() submission handler which upon submission passes the field values to the dataProvider.create() method under the hood. Notice we are registering the fields with React Hook Form's register() method for controlling the fields and emitting error messages.Notice that we are not passing any resource name to useForm(). Like useTable(), it is inferred from the current URL.We are also using the useSelect() core hook to fetch and populate categories items to present inside fields.More on the useSelect() hook in the refine docs here.2. daisyUI StyleWe are using form related daisyUI style classes such as form-control, input, textarea and their variations like input-sm, input-bordered, textarea-bordered, etc.Inside the App.css file, we are still able to compose smaller class names from longer ones. For example, page-container and page-title are custom composed reusable classes that help reduce some Tailwind spaghetti strings.With the above page, when we navigate to the /products/create route, we should be presented with a form to create a product:The product edit page will have the same form functionality as the create page. In addition, it will first send a GET request to load the form fields with existing data. The auto-generated page uses the same useForm() refine-React Hook Form hook. We can modify it to implement the above functionalities. So, copy and replace the code in src/pages/products/edit.tsx with this final one:In the final version of component, we are implementing the same form field control, state management, submission and error handling functionalities as the component implemnted above. We're doing them with the onFinish object, register() and handleSubmit() methods. We are accessing the errors with the formState.errors object. We are also using more or less the same daisyUI classes for buttons and form fields.Additionally, we are setting the current option of the dropdown with setValue() method destructured from useForm() hook.With the component worked out, the page at /product/edit/:id looks like this:The component is more straight forward and the final adopted version looks like this:In the code above, we are using the useShow() hook and grabbing product details from the queryResult object to display the details in the JSX.We are also invoking the familiar useNavigation() hook to access the edit() and list() methods and call them from Edit and back buttons respectively.With the page completed, when we navigate to the /products/show/:id path, we can see the product details as below:This also means we can navigate back and forth to edit and show pages of a product item from the list page. Or open the create page to create new products.The category pages are very similar to the product pages. So, I'll quickly add their final versions. You can copy and paste them directly and use them in your files.Or alternatively, if you want to make your way through generating the code with Inferencers, please feel free to do so.Run the following Inferencer command anyway to produce the scaffold pages inside the src/pages/categories/ directory:Eventually you'll need to update the resources and routes in App.tsx to this:For the final version of , adopt the following code.For the final version of the component, adopt this code:For the final version of the component, adopt this code:For the final version of the page, adopt this code:After all these changes for the category resource, we should be able to navigate across the category pages as below:In this section, we'll customize the app layout for a top navbar menu with icons for each item. In refine, the component is passed to the topmost element, meaning it becomes a common partial to all pages:Currently, the layout places the navigation menu to the left of the page. We want to move it to the top as a navbar.To begin with, we'll remove refine's layout class from there and add some Tailwind classes to move the items to the top. At src/components/layout/index.tsx, make it look like this:Notice it renders the

and components. We'll update them too, but we'll first add icons to each resource in the resources definition in App.tsx. Update the resources array with icons and necessary imports:Adding icons to resources makes them available for menu items as well as breadcrumbs. So, now we'll update the and components with icons and daisyUI styles.Change the component to the below code:Notice now that we are displaying an icon beside each menu item and we are also using the daisyUI menu class to style the menu.Then update component to look like below:Notice also that we are displaying the icons beside the resource name in each breadcrumb. We are using the daisyUI breadcrumbs class to style the elements.With these changes, we should now see a sticky top navbar with items that give access to all the resource list pages. Also, breadcrumbs should show up along a resource path:Here's the walkthrough of all the resource list pages:In this post, we got familiar with how to build a React dashboard and admin panel with refine and daisyUI. We saw how to easily integrate daisyUI with refine's headless core and supplementary packages for React Table and React Hook Form.We used the Fine Foods API for our app. We first built a dashboard page to present several KPI data in cards, charts and a table. While doing so, we learned how to use refine's core useList() hook, and its excellent support for presenting data in tables with useTable() by integrating with React Table. We used Recharts plotting library to create charts of our KPI data. We utilized daisyUI templates with prestyled component classes to style the cards and table.We generated CRUD pages with the Inferencer tool, and we went ahead to further customize them according to our needs. We styled them conveniently with short, semantic, component ready classes offered by daisyUI. We also felt the need to use regular Tailwind Flex, color, size, shape and responsive utility classes, and found that daisyUI offers such flexibility out-of-the-box.We saw that refine brings the power of React Hook Form into its pages with its supplementary @refinedev/react-form-hook package that helps effortlessly manage data fetching, form state, caching and error handling in a CRUD application with the useForm() hook. We found that daisyUI can fit in seamlessly among all to build enterprise level data-heavy applications like admin panels, dashboards and other internal tools.We initially dashed, frequently dazed, and finally established an admin panel by getting refine.d.Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well Confirm For further actions, you may consider blocking this person and/or reporting abuse потик - Oct 1 Thierry Chau - Oct 5 Bhavesh Yadav - Oct 20 OpenSourcee - Oct 1 refine is an open source React framework that enables the rapid development of a wide range of web applications. From internal tools, admin panels, B2B apps and dashboards, it serves as a comprehensive solution for building any type of CRUD applications. Once suspended, refine will not be able to comment or publish posts until their suspension is removed. Once unsuspended, refine will be able to comment and publish posts again. Once unpublished, all posts by refine will become hidden and only accessible to themselves. If refine is not suspended, they can still re-publish their posts from their dashboard. Note: Once unpublished, this post will become invisible to the public and only accessible to Necati Özmen. They can still re-publish the post if they are not suspended. Thanks for keeping DEV Community safe. Here is what you can do to flag refine: refine consistently posts content that violates DEV Community's code of conduct because it is harassing, offensive or spammy. Unflagging refine will restore default visibility to their posts. DEV Community — A constructive and inclusive social network for software developers. With you every step of your journey. Built on Forem — the open source software that powers DEV and other inclusive communities.Made with love and Ruby on Rails. DEV Community © 2016 - 2023. We're a place where coders share, stay up-to-date and grow their careers.



This post first appeared on VedVyas Articles, please read the originial post: here

Share the post

âš¡ Complete Tutorial: React Admin Panel with refine and daisyUI

×

Subscribe to Vedvyas Articles

Get updates delivered right to your inbox!

Thank you for your subscription

×