www/apps/book/app/learn/customization/customize-admin/widget/page.mdx
import { Prerequisites } from "docs-ui"
export const metadata = {
title: ${pageNumber} Guide: Add Product's Brand Widget in Admin,
}
In this chapter, you'll customize the product details page of the Medusa Admin dashboard to show the product's brand. You'll create a widget that is injected into a pre-defined zone in the page, and in the widget you'll retrieve the product's brand from the server and display it.
<Prerequisites items={[ { text: "Brands linked to products", link: "/learn/customization/extend-features/define-link" } ]} />
In your custom widget, you'll retrieve the product's brand by sending a request to the Medusa server. Medusa has a JS SDK that simplifies sending requests to the server's API routes.
So, you'll start by configuring the JS SDK. Create the file src/admin/lib/sdk.ts with the following content:
import Medusa from "@medusajs/js-sdk"
export const sdk = new Medusa({
baseUrl: import.meta.env.VITE_BACKEND_URL || "/",
debug: import.meta.env.DEV,
auth: {
type: "session",
},
})
You initialize the SDK passing it the following options:
baseUrl: The URL to the Medusa server.debug: Whether to enable logging debug messages. This should only be enabled in development.auth.type: The authentication method used in the client application, which is session in the Medusa Admin dashboard.Notice that you use import.meta.env to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in the Admin Environment Variables chapter.
You can now use the SDK to send requests to the Medusa server.
<Note>Refer to the JS SDK Reference to learn more about the it, its options, and how to use it to send requests to Medusa's API routes.
</Note>You'll now add a widget to the product-details page. A widget is a React component that's injected into pre-defined zones in the Medusa Admin dashboard. It's created in a .tsx file under the src/admin/widgets directory.
To create a widget that shows a product's brand in its details page, create the file src/admin/widgets/product-brand.tsx with the following content:
export const highlights = [ ["14", "ProductBrandWidget", "Widget to inject into the page"], ["15", "data", "Receive the product's details as a prop"], ["17", "useQuery", "Use Tanstack Query to send the request to the server with the JS SDK."], ["18", "sdk", "Send the request to retrieve the product with the JS SDK."], ["19", "fields", "Specify the product's brand to be retrieved."], ["23", "brandName", "Get brand name from the query request."], ["53", "defineWidgetConfig", "Export the widget's configurations"], ["54", "zone", "Show the widget at the top of the product details page."] ]
import { defineWidgetConfig } from "@medusajs/admin-sdk"
import { DetailWidgetProps, AdminProduct } from "@medusajs/framework/types"
import { clx, Container, Heading, Text } from "@medusajs/ui"
import { useQuery } from "@tanstack/react-query"
import { sdk } from "../lib/sdk"
type AdminProductBrand = AdminProduct & {
brand?: {
id: string
name: string
}
}
const ProductBrandWidget = ({
data: product,
}: DetailWidgetProps<AdminProduct>) => {
const { data: queryResult } = useQuery({
queryFn: () => sdk.admin.product.retrieve(product.id, {
fields: "+brand.*",
}),
queryKey: [["product", product.id]],
})
const brandName = (queryResult?.product as AdminProductBrand)?.brand?.name
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<div>
<Heading level="h2">Brand</Heading>
</div>
</div>
<div
className={clx(
`text-ui-fg-subtle grid grid-cols-2 items-center px-6 py-4`
)}
>
<Text size="small" weight="plus" leading="compact">
Name
</Text>
<Text
size="small"
leading="compact"
className="whitespace-pre-line text-pretty"
>
{brandName || "-"}
</Text>
</div>
</Container>
)
}
export const config = defineWidgetConfig({
zone: "product.details.before",
})
export default ProductBrandWidget
A widget's file must export:
defineWidgetConfig from the Admin Extension SDK. The function receives an object as a parameter that has a zone property, whose value is the zone to inject the widget to.Since the widget is injected at the top of the product details page, the widget receives the product's details as a parameter.
In the widget, you use Tanstack (React) Query to query the Medusa server. Tanstack Query provides features like asynchronous state management and optimized caching. In the queryFn function that executes the query, you use the JS SDK to send a request to the Get Product API Route, passing +brand.* in the fields query parameter to retrieve the product's brand.
Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install v5.64.2 as a development dependency.
You then render a section that shows the brand's name. In admin customizations, use components from the Medusa UI package to maintain a consistent user interface and design in the dashboard.
To test out your widget, start the Medusa application:
npm run dev
Then, open the admin dashboard at http://localhost:9000/app. After you log in, open the page of a product that has a brand. You'll see a new section at the top showing the brand's name.
When building your widget, you may need more complicated components. For example, you may add a form to the above widget to set the product's brand.
The Admin Components guides show you how to build and use common components in the Medusa Admin, such as forms, tables, JSON data viewer, and more. The components in the guides also follow the Medusa Admin's design convention.
In the next chapter, you'll add a UI route that displays the list of brands in your application and allows admin users.