metropolitan-bronze
metropolitan-bronzeβ€’9mo ago

Refine Sider Modification

I'm working on a Refine project using Material UI and facing a particular challenge. I need to add a custom link in the default sidebar (Sider) that leads to a new page. This page isn't based on a standard resource like in typical Refine usage, but I want to maintain the sidebar's standard features, such as active page highlighting. As far as I understand, there are a couple of ways to customize the Sider: Using the Sider's render prop. Swizzling the entire Sider component for deeper customization. However, I'm seeking a simpler method. I'd like to add this custom link while keeping all the default themes and functionalities of Refine intact. I'm a bit hesitant to use swizzle as it might require ongoing manual updates, which I'd like to avoid. The functionality I need for my custom page includes two search bars that fetch data from my database, send this data to my backend to perform calculations, and then display a list based on those results. I'm wondering if there's a way to integrate this into Refine without defaulting to a standard resource list. Is it possible to use a resource but modify its initial behavior to suit my custom input and backend processing needs, rather than triggering an immediate list fetch? To sum up, I'm looking for an easy way to add a link to the Sider that retains all default functionalities but doesn't rely on a resource. Alternatively, could I customize a resource so that it doesn't immediately trigger a fetch, but instead allows for a POST request with two entities, and then returns a list based on the calculation? Note: I am using the simple REST default. Any guidance or suggestions on this would be really appreciated, especially in terms of maintaining the default styling and functionality of Refine. Thanks in advance for your help!
22 Replies
kapa.ai
kapa.aiβ€’9mo ago
Hi @darshan. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the information provided in the extracts, there are indeed several ways to customize the Sider in Refine to add a custom link. 1. Using the resources property on the <Refine> component: This is the simplest way to add a custom link to the Sider. You can add a dummy resource with a list attribute to the resources prop of the <Refine> component. This will add a navigation link to the sidebar. Here's an example:
import { Refine } from "@refinedev/core";

<Refine
...
resources={[
{
name: "dummy-resource",
list: "/internal-link",
meta: {
label: "Navigation Link",
icon: "πŸ‘‹",
},
},
]}
/>;
import { Refine } from "@refinedev/core";

<Refine
...
resources={[
{
name: "dummy-resource",
list: "/internal-link",
meta: {
label: "Navigation Link",
icon: "πŸ‘‹",
},
},
]}
/>;
Source (https://github.com/refinedev/refine/issues/4051) 2. Using the render property of the <Sider> component: This allows you to render any custom component. Here's an example for Material UI:
import { Layout, Sider } from "@refinedev/mui";

const CustomSider = () => {
return (
<Sider
render={({ items, logout }) => {
return (
<>
<a href="<https://refine.dev/">πŸ‘‹> Navigation Link</a>
{items}
{logout}
</>
);
}}
/>
);
};

const CustomLayout = () => {
return <Layout Sider={CustomSider}>...</Layout>;
};
import { Layout, Sider } from "@refinedev/mui";

const CustomSider = () => {
return (
<Sider
render={({ items, logout }) => {
return (
<>
<a href="<https://refine.dev/">πŸ‘‹> Navigation Link</a>
{items}
{logout}
</>
);
}}
/>
);
};

const CustomLayout = () => {
return <Layout Sider={CustomSider}>...</Layout>;
};
Source (https://refine.dev/docs/guides-concepts/faq#using-render-property-of-sider-component) 3. Using the swizzle command: This allows you to copy the default Sider component to your project and customize it as you want. However, as you mentioned, this might require ongoing manual updates. Source (https://refine.dev/docs/guides-concepts/faq#using-swizzle-command) As for your second question about customizing a resource, unfortunately, the extracts do not provide enough information to give a confident answer. However, Refine does provide a lot of flexibility in terms of customizing resources and their behavior, so it might be possible to achieve what you're looking for. I would recommend checking out the Refine documentation (https://refine.dev/docs) for more information on this topic. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’9mo ago
<@672579770692665354> kapa.ai is still learning and improving, please let us know how it did by reacting below
metropolitan-bronze
metropolitan-bronzeβ€’9mo ago
@kapa.ai I'm using the default simple REST data provider in my Refine project. If I decide to use the 'resource' property, are there any solutions available, that don't include having a custom data provider, to address my requirements? @kapa.ai I'm using the default simple REST data provider in my Refine project. If I decide to use the 'resource' property, are there any solutions available, that don't include having a custom data provider, to address my requirements?
kapa.ai
kapa.aiβ€’9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Based on the information provided in the extracts, using the resource property with the default simple REST data provider in Refine does not inherently allow for the customization you're seeking. The default behavior of a resource in Refine is to trigger a fetch request to retrieve data. However, Refine does provide the ability to use multiple data providers, which could potentially be used to achieve your desired functionality. You could define a custom data provider for your specific resource that handles the POST request and subsequent list fetch based on the calculation. This custom data provider could then be passed to the dataProvider prop of the Refine component, along with your default simple REST data provider. Here's an example of how to define multiple data providers:
import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/simple-rest";

const App: React.FC = () => {
return (
<Refine
dataProvider={{
default: dataProvider("<https://api.fake-rest.refine.dev/">),
other: dataProvider("<https://other-api.fake-rest.refine.dev/">),
}}
>
{/* ... */}
</Refine>
);
};
import { Refine } from "@refinedev/core";
import dataProvider from "@refinedev/simple-rest";

const App: React.FC = () => {
return (
<Refine
dataProvider={{
default: dataProvider("<https://api.fake-rest.refine.dev/">),
other: dataProvider("<https://other-api.fake-rest.refine.dev/">),
}}
>
{/* ... */}
</Refine>
);
};
Source (https://refine.dev/docs/ui-integrations/material-ui/components/basic-views/edit#dataprovidername) In this example, default is the default data provider, and other is a custom data provider. You can specify which data provider to use for a specific resource by using the dataProviderName property. Please note that creating a custom data provider requires a good understanding of how data providers work in Refine. You can refer to the Refine documentation (https://refine.dev/docs/data/data-provider#how-can-i-create-a-custom-data-provider) for more information on this topic. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’9mo ago
<@672579770692665354> kapa.ai is still learning and improving, please let us know how it did by reacting below
metropolitan-bronze
metropolitan-bronzeβ€’9mo ago
@kapa.ai I am working to implement a new page in my sprint this week, so I don't plan on adding a custom data provider. I'm considering two options for the Sider: Using the render property for a custom Sider: Can I push my custom link into the existing item list using this method? And if so, will this approach maintain all the default styling or any modifications to the style context and integration of the Sider? Swizzling the entire Sider component: If I go this route, is it possible to add a custom item to the item list that behaves like the other sidebar items but doesn't rely on a standard resource? Essentially, this means having an item with a Link, an 'is selected' state, and other standard functionalities without the need for a resource. I'm looking for a solution that allows for the addition of a custom link while keeping the sidebar's existing features and style.
kapa.ai
kapa.aiβ€’9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- 1. Using the render property for a custom Sider: Yes, you can push your custom link into the existing item list using this method. The render property is a function that receives an object with the items and logout properties. The items property is the list of navigation items and the logout property is the logout button. You can add your custom link to this list. Here's an example:
import { Layout, Sider } from "@refinedev/mui";

const CustomSider = () => {
return (
<Sider
render={({ items, logout }) => {
return (
<>
<a href="<https://refine.dev/">πŸ‘‹> Navigation Link</a>
{items}
{logout}
</>
);
}}
/>
);
};

const CustomLayout = () => {
return <Layout Sider={CustomSider}>...</Layout>;
};
import { Layout, Sider } from "@refinedev/mui";

const CustomSider = () => {
return (
<Sider
render={({ items, logout }) => {
return (
<>
<a href="<https://refine.dev/">πŸ‘‹> Navigation Link</a>
{items}
{logout}
</>
);
}}
/>
);
};

const CustomLayout = () => {
return <Layout Sider={CustomSider}>...</Layout>;
};
Source (https://refine.dev/docs/guides-concepts/faq#using-render-property-of-sider-component) This approach should maintain all the default styling or any modifications to the style context and integration of the Sider, as you're only adding a custom link to the existing list of items. 2. Swizzling the entire Sider component: If you choose to swizzle the entire Sider component, you can indeed add a custom item to the item list that behaves like the other sidebar items but doesn't rely on a standard resource. Swizzling allows you to customize the components and use your own components, so you can add any functionality you want. However, as you mentioned, this might require ongoing manual updates. Source (https://refine.dev/docs/packages/cli#swizzle) In both cases, you should be able to add a custom link while keeping the sidebar's existing features and style. However, the render property method might be simpler and easier to maintain in the long run. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’9mo ago
<@672579770692665354> kapa.ai is still learning and improving, please let us know how it did by reacting below
metropolitan-bronze
metropolitan-bronzeβ€’9mo ago
@kapa.ai I guess I can have a 'dummy' resource that just returns an empty list, and then do whatever I need to do on the page. It just seems weird but I guess can be an okay MVP solution for my sprint
kapa.ai
kapa.aiβ€’9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Yes, you're correct. Creating a 'dummy' resource that returns an empty list is a viable solution for your needs. This approach allows you to add a link to the sidebar without having to heavily customize the Sider or create a custom data provider. Here's an example of how you can add a dummy resource:
import { Refine } from "@refinedev/core";

<Refine
...
resources={[
{
name: "dummy-resource",
list: "/internal-link",
meta: {
label: "Navigation Link",
icon: <span>πŸ‘‹</span>,
},
},
]}
/>;
import { Refine } from "@refinedev/core";

<Refine
...
resources={[
{
name: "dummy-resource",
list: "/internal-link",
meta: {
label: "Navigation Link",
icon: <span>πŸ‘‹</span>,
},
},
]}
/>;
Source (https://github.com/refinedev/refine/issues/4051) In this example, dummy-resource is the name of the dummy resource, /internal-link is the path to your custom page, and Navigation Link is the label that will be displayed in the sidebar. Once you've added the dummy resource, you can then customize the page as needed to implement your specific functionality. This approach should be sufficient for an MVP solution and can be refined further in future sprints. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’9mo ago
<@672579770692665354> kapa.ai is still learning and improving, please let us know how it did by reacting below
metropolitan-bronze
metropolitan-bronzeβ€’9mo ago
@kapa.ai Thank you, I will go with that approach for now. I assume the internal link doesn't need to be set up in my backend because the endpoint for the list is only called when hooks that use a resource are invoked, is that correct? Also, I can use other Refine hooks for custom endpoints and make whatever POST calls I need. Could you refer me to a Refine hook that allows for custom POST methods and such?
kapa.ai
kapa.aiβ€’9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Yes, you're correct. The internal link doesn't need to be set up in your backend because the endpoint for the list is only called when hooks that use a resource are invoked. For custom POST methods, you can use the useCustom hook provided by Refine. This hook allows you to make custom API calls. Here's an example of how to use it:
import { useCustom, useApiUrl } from "@refinedev/core";

const { data, isLoading } = useCustom({
url: `${apiURL}/posts-unique-check`,
method: "get",
config: {
query: {
title: "Foo bar",
},
},
});
import { useCustom, useApiUrl } from "@refinedev/core";

const { data, isLoading } = useCustom({
url: `${apiURL}/posts-unique-check`,
method: "get",
config: {
query: {
title: "Foo bar",
},
},
});
Source (https://refine.dev/docs/tutorial/understanding-dataprovider/create-dataprovider#custom) In this example, useCustom is used to make a GET request to a custom endpoint. You can change the method property to "post" to make a POST request, and you can add a payload property to the config object to send data with the request. Please note that the useCustom hook is a powerful tool that can be used to make any type of API call, not just POST requests. You can refer to the Refine documentation (https://refine.dev/docs/data/hooks/use-custom/) for more information on this hook. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’9mo ago
<@672579770692665354> kapa.ai is still learning and improving, please let us know how it did by reacting below
metropolitan-bronze
metropolitan-bronzeβ€’9mo ago
@kapa.ai Perfect, is there a hook I can use to call a custom POST method that includes properties compatible with DataGridPro? For instance, one that provides the necessary DataGrid props and such?
kapa.ai
kapa.aiβ€’9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Based on the information provided in the extracts, the useDataGrid hook in Refine is used to get properties that are compatible with MUI X's DataGrid and DataGridPro components. This hook fetches data from the API and wraps them with various helper hooks required for Material UI components. Here's an example of how to use it:
import React from "react";
import { useDataGrid, DataGrid, List } from "@refinedev/mui";

const { dataGridProps } = useDataGrid<IPost>();
import React from "react";
import { useDataGrid, DataGrid, List } from "@refinedev/mui";

const { dataGridProps } = useDataGrid<IPost>();
Source (https://refine.dev/blog/dynamic-forms-in-react-hook-form#listing-records​) However, the useDataGrid hook is designed to fetch data from the API, not to make custom POST requests. For custom POST requests, you can use the useCustom hook as mentioned in the previous response. Unfortunately, the useCustom hook does not provide properties that are compatible with DataGridPro. If you need to make a custom POST request and then display the returned data in a DataGridPro component, you might need to use the useCustom hook to make the request and then manually pass the returned data to the DataGridPro component. Please note that this approach requires a good understanding of how hooks and components work in React and Refine. You can refer to the Refine documentation (https://refine.dev/docs) for more information on this topic. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’9mo ago
<@672579770692665354> kapa.ai is still learning and improving, please let us know how it did by reacting below
metropolitan-bronze
metropolitan-bronzeβ€’9mo ago
@kapa.ai I'm a bit rusty with React. Is there a way I can use the useCustom hook for a debounced search in a search bar, where results are displayed as I type? How can I achieve this with the useCustom hook? Also, Refine uses TanStack, correct?
kapa.ai
kapa.aiβ€’9mo ago
Let me check the knowledge base for you. One moment! :books: ---------------------- Yes, you can use the useCustom hook in Refine for a debounced search in a search bar. The useCustom hook allows you to make custom API calls, and you can use it to fetch search results based on the user's input. Here's a basic example of how you might use the useCustom hook for a debounced search:
import { useCustom, useApiUrl } from "@refinedev/core";
import { useState, useEffect } from 'react';
import debounce from 'lodash.debounce';

const SearchBar = () => {
const [searchTerm, setSearchTerm] = useState("");
const apiUrl = useApiUrl();

const { data, isLoading } = useCustom({
url: `${apiUrl}/search-endpoint`,
method: "get",
config: {
query: {
term: searchTerm,
},
},
});

const debouncedSearchTerm = debounce(setSearchTerm, 500);

useEffect(() => {
return () => {
debouncedSearchTerm.cancel();
};
}, []);

const handleSearch = (event) => {
debouncedSearchTerm(event.target.value);
};

return (
<input type="text" onChange={handleSearch} />
);
};
import { useCustom, useApiUrl } from "@refinedev/core";
import { useState, useEffect } from 'react';
import debounce from 'lodash.debounce';

const SearchBar = () => {
const [searchTerm, setSearchTerm] = useState("");
const apiUrl = useApiUrl();

const { data, isLoading } = useCustom({
url: `${apiUrl}/search-endpoint`,
method: "get",
config: {
query: {
term: searchTerm,
},
},
});

const debouncedSearchTerm = debounce(setSearchTerm, 500);

useEffect(() => {
return () => {
debouncedSearchTerm.cancel();
};
}, []);

const handleSearch = (event) => {
debouncedSearchTerm(event.target.value);
};

return (
<input type="text" onChange={handleSearch} />
);
};
In this example, the lodash.debounce function is used to debounce the search term. The useEffect hook is used to cancel the debounced function when the component is unmounted. The handleSearch function is called whenever the user types in the search bar, and it updates the search term with a delay of 500 milliseconds. Please note that this is a simplified example and you might need to adjust it based on your specific requirements and API. And yes, you're correct. Refine uses TanStack Query under the hood for data fetching and state management. This provides a lot of out-of-the-box functionality for managing data in a structured and efficient manner. Source (https://refine.dev/docs/guides-concepts/data-fetching#how-refine-treats-data-and-state) ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.aiβ€’9mo ago
<@672579770692665354> kapa.ai is still learning and improving, please let us know how it did by reacting below
plain-purple
plain-purpleβ€’9mo ago
Hello @darshan Unfortunately there is no customize option other than the ones you suggested and I think it remains very much an application-specific case. I think it would be healthier to implement your own sider. you can use useMenu for this https://refine.dev/docs/core/hooks/utilities/use-menu/
metropolitan-bronze
metropolitan-bronzeβ€’9mo ago
Hello @Alican Erdurmaz thank you for the advice and the useMenu link – much appreciated!