+
+
+
+ Name
+
+
+
+ {brandName || "-"}
+
+
+
+ )
+}
+
+export const config = defineWidgetConfig({
+ zone: "product.details.before",
+})
+
+export default ProductBrandWidget
+```
+
+A widget's file must export:
+
+- A React component to be rendered in the specified injection zone. The component must be the file's default export.
+- A configuration object created with `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](https://tanstack.com/query/latest) 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](undefined/api/admin#products_getproductsid), 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](undefined/ui) to maintain a consistent user interface and design in the dashboard.
+
+***
+
+## Test it Out
+
+To test out your widget, start the Medusa application:
+
+```bash npm2yarn
+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.
+
+
+
+***
+
+## Admin Components Guides
+
+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](undefined/resources/admin-components) 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.
+
+***
+
+## Next Chapter: Add UI Route for Brands
+
+In the next chapter, you'll add a UI route that displays the list of brands in your application and allows admin users.
+
+
+# Create Brands UI Route in Admin
+
+In this chapter, you'll add a UI route to the admin dashboard that shows all [brands](undefined/learn/customization/custom-features/module) in a new page. You'll retrieve the brands from the server and display them in a table with pagination.
+
+### Prerequisites
+
+- [Brands Module](undefined/learn/customization/custom-features/modules)
+
+## 1. Get Brands API Route
+
+In a [previous chapter](undefined/learn/customization/extend-features/query-linked-records), you learned how to add an API route that retrieves brands and their products using [Query](undefined/learn/fundamentals/module-links/query). You'll expand that API route to support pagination, so that on the admin dashboard you can show the brands in a paginated table.
+
+Replace or create the `GET` API route at `src/api/admin/brands/route.ts` with the following:
+
+```ts title="src/api/admin/brands/route.ts" highlights={apiRouteHighlights}
+// other imports...
+import {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+
+export const GET = async (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ const query = req.scope.resolve("query")
+
+ const {
+ data: brands,
+ metadata: { count, take, skip } = {},
+ } = await query.graph({
+ entity: "brand",
+ ...req.queryConfig,
+ })
+
+ res.json({
+ brands,
+ count,
+ limit: take,
+ offset: skip,
+ })
+}
+```
+
+In the API route, you use Query's `graph` method to retrieve the brands. In the method's object parameter, you spread the `queryConfig` property of the request object. This property holds configurations for pagination and retrieved fields.
+
+The query configurations are combined from default configurations, which you'll add next, and the request's query parameters:
+
+- `fields`: The fields to retrieve in the brands.
+- `limit`: The maximum number of items to retrieve.
+- `offset`: The number of items to skip before retrieving the returned items.
+
+When you pass pagination configurations to the `graph` method, the returned object has the pagination's details in a `metadata` property, whose value is an object having the following properties:
+
+- `count`: The total count of items.
+- `take`: The maximum number of items returned in the `data` array.
+- `skip`: The number of items skipped before retrieving the returned items.
+
+You return in the response the retrieved brands and the pagination configurations.
+
+Learn more about pagination with Query in [this chapter](undefined/learn/fundamentals/module-links/query#apply-pagination).
+
+***
+
+## 2. Add Default Query Configurations
+
+Next, you'll set the default query configurations of the above API route and allow passing query parameters to change the configurations.
+
+Medusa provides a `validateAndTransformQuery` middleware that validates the accepted query parameters for a request and sets the default Query configuration. So, in `src/api/middlewares.ts`, add a new middleware configuration object:
+
+```ts title="src/api/middlewares.ts"
+import {
+ defineMiddlewares,
+ validateAndTransformQuery,
+} from "@medusajs/framework/http"
+import { createFindParams } from "@medusajs/medusa/api/utils/validators"
+// other imports...
+
+export const GetBrandsSchema = createFindParams()
+
+export default defineMiddlewares({
+ routes: [
+ // ...
+ {
+ matcher: "/admin/brands",
+ method: "GET",
+ middlewares: [
+ validateAndTransformQuery(
+ GetBrandsSchema,
+ {
+ defaults: [
+ "id",
+ "name",
+ "products.*",
+ ],
+ isList: true,
+ }
+ ),
+ ],
+ },
+
+ ],
+})
+```
+
+You apply the `validateAndTransformQuery` middleware on the `GET /admin/brands` API route. The middleware accepts two parameters:
+
+- A [Zod](https://zod.dev/) schema that a request's query parameters must satisfy. Medusa provides `createFindParams` that generates a Zod schema with the following properties:
+ - `fields`: A comma-separated string indicating the fields to retrieve.
+ - `limit`: The maximum number of items to retrieve.
+ - `offset`: The number of items to skip before retrieving the returned items.
+ - `order`: The name of the field to sort the items by. Learn more about sorting in [the API reference](undefined/api/admin#sort-order)
+- An object of Query configurations having the following properties:
+ - `defaults`: An array of default fields and relations to retrieve.
+ - `isList`: Whether the API route returns a list of items.
+
+By applying the above middleware, you can pass pagination configurations to `GET /admin/brands`, which will return a paginated list of brands. You'll see how it works when you create the UI route.
+
+Learn more about using the `validateAndTransformQuery` middleware to configure Query in [this chapter](undefined/learn/fundamentals/module-links/query#request-query-configurations).
+
+***
+
+## 3. Initialize JS SDK
+
+In your custom UI route, you'll retrieve the brands by sending a request to the Medusa server. Medusa has a [JS SDK](undefined/resources/js-sdk) that simplifies sending requests to the core API route.
+
+If you didn't follow the [previous chapter](undefined/learn/customization/customize-admin/widget), create the file `src/admin/lib/sdk.ts` with the following content:
+
+
+
+```ts title="src/admin/lib/sdk.ts"
+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 [this chapter](undefined/learn/fundamentals/admin/environment-variables).
+
+You can now use the SDK to send requests to the Medusa server.
+
+Learn more about the JS SDK and its options in [this reference](undefined/resources/js-sdk).
+
+***
+
+## 4. Add a UI Route to Show Brands
+
+You'll now add the UI route that shows the paginated list of brands. A UI route is a React component created in a `page.tsx` file under a sub-directory of `src/admin/routes`. The file's path relative to src/admin/routes determines its path in the dashboard.
+
+Learn more about UI routes in [this chapter](undefined/learn/fundamentals/admin/ui-routes).
+
+So, to add the UI route at the `localhost:9000/app/brands` path, create the file `src/admin/routes/brands/page.tsx` with the following content:
+
+
+
+```tsx title="src/admin/routes/brands/page.tsx" highlights={uiRouteHighlights}
+import { defineRouteConfig } from "@medusajs/admin-sdk"
+import { TagSolid } from "@medusajs/icons"
+import {
+ Container,
+} from "@medusajs/ui"
+import { useQuery } from "@tanstack/react-query"
+import { sdk } from "../../lib/sdk"
+import { useMemo, useState } from "react"
+
+const BrandsPage = () => {
+ // TODO retrieve brands
+
+ return (
+