Tailwind Example/Boilerplate

Anyone have a Tailwind 4.0 and Refine Example app public? We are picking shadcn and tailwind for all our design systems. And wanted to see if anyone was already using it.
17 Replies
kapa.ai
kapa.ai3w ago
Hi @mjd. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Yes, there are several examples of using Tailwind CSS with Refine! Refine works well with Tailwind CSS for styling your applications, and there are multiple resources showing this integration. Refine's headless architecture makes it easy to integrate with UI frameworks like Tailwind CSS. Here are some examples from the knowledge sources: 1. There's a blog post about Creating a Responsive React Navbar with Tailwind CSS that demonstrates how to use Tailwind with Refine. This includes a complete example that you can run locally with:
npm create refine-app@latest -- --example blog-responsive-navbar
npm create refine-app@latest -- --example blog-responsive-navbar
2. Another example shows Building a CRUD app with Shadcn UI and Refine, which is particularly relevant since you mentioned wanting to use shadcn. This demonstrates how Shadcn (which is built on Tailwind) can be integrated seamlessly with Refine. 3. There's also documentation on setting up Tailwind CSS in a Refine project, which includes the basic configuration steps. For your specific interest in shadcn and Tailwind with Refine 4.0, the Shadcn UI blog post would be most relevant as it shows how to configure Tailwind CSS with Refine and integrate Shadcn components, which aligns with your design system choice. The basic setup for Tailwind in a Refine project involves:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
Then configuring your tailwind.config.js and adding the Tailwind directives to your CSS. Would you like more specific information about any of these integration examples? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@752243518545854624> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
evident-indigo
evident-indigoOP3w ago
@kapa.ai Would we say that AntD is the preferred theme library for Refine?
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Yes, Ant Design (AntD) can be considered the preferred UI framework for Refine, though Refine is designed to be headless and work with multiple UI frameworks. Ant Design has first-class integration with Refine and is often showcased in Refine's examples and documentation. Here's why it appears to be the preferred option: 1. Refine provides a dedicated integration package for Ant Design that offers ready-to-use components and hooks specifically designed to connect Refine with Ant Design components Documentation > Docs > Ui-integrations > Ant-design. 2. Many of Refine's examples and tutorials prominently feature Ant Design, such as the React CRM app example Building a Complete React CRM App with Refine, Ant Design and GraphQL. 3. Refine offers predefined themes specifically for Ant Design through the RefineThemes from @refinedev/antd Documentation > Docs > Examples > Themes. 4. When creating a new Refine project using the CLI tool, Ant Design is presented as one of the primary UI framework options Setting Up the Client App. However, it's important to note that Refine is fundamentally headless and supports multiple UI frameworks:
"Refine is a headless React-based framework... it can be used without being tied to any UI elements or framework." Top React Component Libraries and Frameworks Every Developer Should Know
Refine also has built-in support for Material UI, Mantine, and Chakra UI, as mentioned in the comparison table Documentation > Docs > Further-readings > Comparison. So while Ant Design appears to be the most prominently featured UI framework in Refine's documentation and examples, you have the flexibility to choose other UI frameworks based on your project requirements. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@752243518545854624> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
evident-indigo
evident-indigoOP3w ago
@kapa.ai How can I reference vaules from a useShow hook in sub components of the <Show> tag
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- To reference values from the useShow hook in sub-components of the <Show> tag in refine, you need to extract the data from the hook in your parent component and then pass it down to child components. Here's how you can do it: Basic Pattern The useShow hook returns a query result that contains the data for the record you're viewing. You can extract this data and pass it to child components:
import { useShow } from "@refinedev/core";
import { Show } from "@refinedev/antd"; // or any other UI package

const ParentComponent = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show isLoading={isLoading}>
<ChildComponent record={record} />
<AnotherChildComponent record={record} />
</Show>
);
};

const ChildComponent = ({ record }) => {
return <div>{record?.title}</div>;
};
import { useShow } from "@refinedev/core";
import { Show } from "@refinedev/antd"; // or any other UI package

const ParentComponent = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show isLoading={isLoading}>
<ChildComponent record={record} />
<AnotherChildComponent record={record} />
</Show>
);
};

const ChildComponent = ({ record }) => {
return <div>{record?.title}</div>;
};
Examples with Different UI Frameworks Ant Design Example
import { useShow } from "@refinedev/core";
import { Show, MarkdownField } from "@refinedev/antd";
import { Typography } from "antd";

const { Title, Text } = Typography;

const PostShow = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show isLoading={isLoading}>
<Title level={5}>Id</Title>
<Text>{record?.id}</Text>

<Title level={5}>Title</Title>
<Text>{record?.title}</Text>

<CustomComponent record={record} />
</Show>
);
};

const CustomComponent = ({ record }) => {
// Use record data here
return <div>Custom view of {record?.title}</div>;
};
import { useShow } from "@refinedev/core";
import { Show, MarkdownField } from "@refinedev/antd";
import { Typography } from "antd";

const { Title, Text } = Typography;

const PostShow = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show isLoading={isLoading}>
<Title level={5}>Id</Title>
<Text>{record?.id}</Text>

<Title level={5}>Title</Title>
<Text>{record?.title}</Text>

<CustomComponent record={record} />
</Show>
);
};

const CustomComponent = ({ record }) => {
// Use record data here
return <div>Custom view of {record?.title}</div>;
};
Ant Design Show Component Material UI Example
import { useShow } from "@refinedev/core";
import { Show, TextField } from "@refinedev/mui";
import { Typography, Stack } from "@mui/material";

const ProductShow = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show isLoading={isLoading}>
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
Id
</Typography>
<TextField value={record?.id ?? ""} />

<DetailSection record={record} />
</Stack>
</Show>
);
};

const DetailSection = ({ record }) => {
// Use record data here
return (
<>
<Typography variant="body1" fontWeight="bold">
Additional Details
</Typography>
<div>{record?.additionalInfo}</div>
</>
);
};
import { useShow } from "@refinedev/core";
import { Show, TextField } from "@refinedev/mui";
import { Typography, Stack } from "@mui/material";

const ProductShow = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show isLoading={isLoading}>
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
Id
</Typography>
<TextField value={record?.id ?? ""} />

<DetailSection record={record} />
</Stack>
</Show>
);
};

const DetailSection = ({ record }) => {
// Use record data here
return (
<>
<Typography variant="body1" fontWeight="bold">
Additional Details
</Typography>
<div>{record?.additionalInfo}</div>
</>
);
};
Material UI Introduction Chakra UI Example
import { useShow } from "@refinedev/core";
import { Show, TextField } from "@refinedev/chakra-ui";
import { Heading } from "@chakra-ui/react";

const ProductShow = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show isLoading={isLoading}>
<Heading as="h5" size="sm">
Id
</Heading>
<TextField value={record?.id} />

<RelatedInfo record={record} />
</Show>
);
};

const RelatedInfo = ({ record }) => {
// Use record data here
return (
<>
<Heading as="h5" size="sm" mt={4}>
Related Information
</Heading>
<div>{record?.relatedData}</div>
</>
);
};
import { useShow } from "@refinedev/core";
import { Show, TextField } from "@refinedev/chakra-ui";
import { Heading } from "@chakra-ui/react";

const ProductShow = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show isLoading={isLoading}>
<Heading as="h5" size="sm">
Id
</Heading>
<TextField value={record?.id} />

<RelatedInfo record={record} />
</Show>
);
};

const RelatedInfo = ({ record }) => {
// Use record data here
return (
<>
<Heading as="h5" size="sm" mt={4}>
Related Information
</Heading>
<div>{record?.relatedData}</div>
</>
);
};
Chakra UI Introduction Important Notes 1. In refine v4+, the recommended way to access the data is through the query property rather than queryResult:
const { query } = useShow();
const { data, isLoading } = query;
const record = data?.data;
const { query } = useShow();
const { data, isLoading } = query;
const record = data?.data;
2. The useShow hook automatically reads the resource and ID from the current URL, but you can also specify them explicitly if needed. 3. You can also use the useOne hook in child components to fetch related data based on the record's ID or other properties. This pattern allows you to organize your code better by separating the data fetching logic in the parent component from the presentation logic in child components. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@752243518545854624> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
evident-indigo
evident-indigoOP3w ago
@kapa.ai I dont want to prop drill. Is there another way
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- If you want to avoid prop drilling when using the useShow hook in refine, there are a couple of alternative approaches you can use: 1. Call useShow in Each Component You can call the useShow hook directly in any component that needs the data, even in child components. Since the hook reads the resource and ID from the URL by default, it will return the same data:
import { useShow } from "@refinedev/core";

const ChildComponent = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return <div>{record?.title}</div>;
};
import { useShow } from "@refinedev/core";

const ChildComponent = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return <div>{record?.title}</div>;
};
This approach is simple but has the downside of making multiple identical queries. However, TanStack Query (which refine uses under the hood) will deduplicate these requests, so only one network request is actually made. 2. Create a Context Provider For a more elegant solution, you can create a context to share the data from useShow:
import { createContext, useContext } from "react";
import { useShow } from "@refinedev/core";
import { Show } from "@refinedev/chakra-ui"; // or your preferred UI package

// Create a context
const ShowContext = createContext();

// Create a provider component
const ShowProvider = ({ children }) => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<ShowContext.Provider value={{ record, isLoading }}>
{children}
</ShowContext.Provider>
);
};

// Create a hook to use the context
const useShowContext = () => useContext(ShowContext);

// Main component
const ProductShow = () => {
const { isLoading } = useShowContext();

return (
<ShowProvider>
<Show isLoading={isLoading}>
<ChildComponent />
<AnotherChildComponent />
</Show>
</ShowProvider>
);
};
// Child components
const ChildComponent = () => {
const { record } = useShowContext();
return <div>{record?.title}</div>;
};

const AnotherChildComponent = () => {
const { record } = useShowContext();
return <div>{record?.description}</div>;
};
import { createContext, useContext } from "react";
import { useShow } from "@refinedev/core";
import { Show } from "@refinedev/chakra-ui"; // or your preferred UI package

// Create a context
const ShowContext = createContext();

// Create a provider component
const ShowProvider = ({ children }) => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<ShowContext.Provider value={{ record, isLoading }}>
{children}
</ShowContext.Provider>
);
};

// Create a hook to use the context
const useShowContext = () => useContext(ShowContext);

// Main component
const ProductShow = () => {
const { isLoading } = useShowContext();

return (
<ShowProvider>
<Show isLoading={isLoading}>
<ChildComponent />
<AnotherChildComponent />
</Show>
</ShowProvider>
);
};
// Child components
const ChildComponent = () => {
const { record } = useShowContext();
return <div>{record?.title}</div>;
};

const AnotherChildComponent = () => {
const { record } = useShowContext();
return <div>{record?.description}</div>;
};
This approach allows any nested component to access the record data without prop drilling, while still maintaining a single data fetch. Both approaches are valid in refine, and the choice depends on your specific requirements and component structure. The context approach is generally more maintainable for complex component trees, while directly using useShow in each component is simpler for smaller applications. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@752243518545854624> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
evident-indigo
evident-indigoOP3w ago
@kapa.ai when will refine support mui 7.0
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Based on the knowledge sources, there's no specific information about when refine will support Material UI 7.0. However, I can provide some relevant context about refine's Material UI support: Currently, refine has recently added support for Material UI 6.0 and X Data Grid 7.0, as evidenced by the migration guide available in the documentation Migration Guide for Material UI v6 and X Data Grid v7. According to a Discord support thread, the refine team planned to release MUI 6 support in their December 5th release cycle (presumably 2023), which has already happened:
"Hey Community Member 100 we planto release MUI 6 in the nexy release cycle, which is December 5th" Discord support thread: MUI 6
Since Material UI 7.0 hasn't been released by the MUI team yet (they just released v6), there's no specific timeline mentioned in the knowledge sources for when refine will support MUI 7.0. For context, refine typically maintains support for major UI frameworks and updates their integration packages when new versions are released. The current version alignment shows: | @refinedev/mui | @mui/material | | --- | --- | | 5.x.x | 5.x.x | | 6.x.x | 6.x.x | If you need the latest Material UI support, I recommend using the current refine MUI 6 integration or checking the official refine GitHub repository or Discord for the most up-to-date information about upcoming support for future MUI versions. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@752243518545854624> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
evident-indigo
evident-indigoOP3w ago
@kapa.ai Im using @refinedev/nestjsx-crud. When doing an initialSort it formats the sort query "sort[0]=id,ASC". But the server side doesnt honor the array addition to the sort attribute
kapa.ai
kapa.ai3w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I understand you're having an issue with sorting in @refinedev/nestjsx-crud where the query parameter format doesn't match what your server expects. Based on the knowledge sources, this appears to be a known issue with how the data provider formats sort parameters. The Hasura data provider had a similar issue where the sort parameters weren't being generated correctly for the server to process them properly, as mentioned in this GitHub issue. For NestJSX CRUD specifically, the data provider is generating sort parameters in the format sort[0]=id,ASC, but your server doesn't recognize this array notation in the sort parameter. There are a few approaches to solve this: 1. Override the data provider method: You can customize how the sort parameters are formatted by overriding the getList method of the data provider. This is mentioned in a few GitHub issues as a solution for similar problems:
import { DataProvider } from "@refinedev/nestjsx-crud";

const originalDataProvider = DataProvider("YOUR_API_URL");

const customDataProvider = {
...originalDataProvider,
getList: async ({ resource, pagination, filters, sorters, meta }) => {
// Modify the sorters format here to match what your server expects
// For example, if your server expects sort=id,ASC instead of sort[0]=id,ASC

return originalDataProvider.getList({
resource,
pagination,
filters,
sorters,
meta: {
...meta,
// You might need to add custom query params here
},
});
},
};

import { DataProvider } from "@refinedev/nestjsx-crud";

const originalDataProvider = DataProvider("YOUR_API_URL");

const customDataProvider = {
...originalDataProvider,
getList: async ({ resource, pagination, filters, sorters, meta }) => {
// Modify the sorters format here to match what your server expects
// For example, if your server expects sort=id,ASC instead of sort[0]=id,ASC

return originalDataProvider.getList({
resource,
pagination,
filters,
sorters,
meta: {
...meta,
// You might need to add custom query params here
},
});
},
};

2. Client-side sorting: If modifying the server or data provider isn't an option, you can use client-side sorting as described in the documentation:
import { useTable } from "@refinedev/core";

const { tableQuery, sorters } = useTable({
sorters: {
mode: "off", // This disables server-side sorting
initial: [{ field: "id", order: "asc" }]
},
});

// Then sort the data client-side
const sortedData = useMemo(() => {
if (sorters.length === 0) {
return tableQuery.data;
}

return tableQuery.data.sort((a, b) => {
const sorter = sorters[0];
if (sorter.order === "asc") {
return a[sorter.field] > b[sorter.field] ? 1 : -1;
} else {
return a[sorter.field] < b[sorter.field] ? 1 : -1;
}
});
}, [tableQuery.data, sorters]);

import { useTable } from "@refinedev/core";

const { tableQuery, sorters } = useTable({
sorters: {
mode: "off", // This disables server-side sorting
initial: [{ field: "id", order: "asc" }]
},
});

// Then sort the data client-side
const sortedData = useMemo(() => {
if (sorters.length === 0) {
return tableQuery.data;
}

return tableQuery.data.sort((a, b) => {
const sorter = sorters[0];
if (sorter.order === "asc") {
return a[sorter.field] > b[sorter.field] ? 1 : -1;
} else {
return a[sorter.field] < b[sorter.field] ? 1 : -1;
}
});
}, [tableQuery.data, sorters]);

3. Modify your server: If you have control over the server, you could update it to handle the array notation in sort parameters. The most common approach based on the knowledge sources is to override the data provider method, as mentioned in this GitHub issue response where a similar solution was suggested for a GraphQL data provider. If you need more specific guidance, you might want to share more details about your server implementation and what format it expects for sort parameters. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3w ago
<@752243518545854624> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?