Creating an Odoo category page using React.js involves creating an Odoo headless ecommerce solution that communicates with Odoo’s backend, usually through its Odoo REST API.
Introduction
Odoo Headless development is a system that splits the frontend and backend of an Odoo e-commerce site, thereby improving its efficiency, speed and user-friendliness.
This separation improves scalability by utilizing the strengths of each part, leading to a better and more enjoyable user experience.
Webkul Odoo REST API offers a variety of API endpoints that allow you to perform different actions such as ‘products’, ‘category-list’, And ‘cart’ but we will work with the ‘category-list’ period.
To start
We will guide you step by step through this blog on how to create a category page using Odoo REST API.
- Install Tailwind CSS
- Configure the React router
- Configuring the Odoo API
- Creating the Category Page Component
- Component integration
- Run your application
Assumption: We are familiar with the Create React app and you have configured the React app.
Note: We will set up the project with the name “odoo category page.“
Step 1: Install Tailwind CSS
You need to implement Tailwind CSS for effective page UI design.
1. Install tailwind CSS via npm: This command installs tailwind CSS and Dev dependencies. run the following command:
npm install -D tailwindcss postcss autoprefixer
2. Initialize Tailwind CSS: This command creates a configuration file for Tailwind CSS.
npx tailwindcss init -p
3. Configure Tailwind CSS: Open THE tailwind.config.js
file and replace the contents with the following configuration to set the purge paths for your files:
/** @type {import('tailwindcss').Config} */ module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", // Adjust if needed ], theme: { extend: { colors: { primary: "#35979C", }, }, }, plugins: [], }
4. Import Tailwind CSS: In the src directory, open the index.css file and add the following lines to import the tailwind file base, componentsAnd utilities styles.
@tailwind base; @tailwind components; @tailwind utilities;
Overall, configure the project and you can see the final folder structure.
. ├── src/ │ ├── App.js │ ├── App.css │ ├── App.test.js | ├── index.js | ├── index.css │ └── logo.svg ├── public/ │ ├── index.html │ ├── logo.svg │ ├── robots.txt │ └── manifest.json ├── package-lock.json ├── package.json ├── tailwind.config.js └── README.md
Step 2: Configure React Router
React Router is used to define multiple routes in the application, so you need to install the React router library.
1. Install React router: Run the following command in your project directory.
npm install react-router-dom
2. Mount the React router: Open App.js
Classify and replace the code to import BrowserRouter, Route
And Routes
to display the router component.
import "./App.css"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import Layout from "./pages/Layout"; function App() { return ( <> <BrowserRouter> <Routes> <Route path="/" element={<Layout />}> <Route index element={<h1> Render home page </h1>} /> </Route> </Routes> </BrowserRouter> </> ); } export default App;
After that you can create the category page layout and other pages like Odoo basket And Odoo product sheet.
We will start by creating a file named Layout.js
in the src/pages
case.
import { Outlet } from "react-router-dom"; import Header from "../components/Header"; import Footer from "../components/Footer"; const Layout = () => { return ( <> {/* You can add the Header Here */} <Header /> <div className="container max-w-[74%] mx-auto min-h-[60vh]"> <Outlet /> </div> {/* You can add the Footer Here */} <Footer /> </> ); }; export default Layout;
Step 3: Configuring Odoo API
1. Configuring Odoo: Make sure you have access to your Odoo instance and API authentication token.
const ODOO_URL = "Odoo_API_URL"; const Authenticate = "AUTH_TOKEN";
2. Create an API helper: Create a file named index.js
to handle API requests in the src/fetcher directory.
const ODOO_URL = "Odoo_API_URL"; const Authenticate = "AUTH_TOKEN"; const callOdooApi = async ({ query, method = "GET", variables }) => { try { const response = await fetch(`${ODOO_URL}/${query}`, { method: method, headers: { "Content-Type": "application/json", Authenticate: Authenticate, }, bosy: JSON.stringify({ ...(variables && variables), }), }); const body = await response.json(); if (body.errors) { throw body.errors[0]; } return { status: response.status, body, }; } catch (e) { console.log(e); } }; export const fetchCategories = async () => { const res = await callOdooApi({ query: "category-list", method: "POST", }); const result = res.body; return { result }; }; export const getCollectionProducts = async (category) => { const handle = { filter: { category_url: { eq: category } }, search: "", pageSize: 12, currentPage: 1, }; const res = await callOdooApi({ query: "product-list", method: "POST", variables: { ...handle, }, }); const result = res.body; return { result }; };
Step 4: Creating the Category Page Component
1.Create a category component: Create a new file Category.js
in the src/pages/shop/category.js
import { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { fetchCategories, getCollectionProducts } from "../../../fetcher"; const Category = () => { const { categorySlug } = useParams(); const [products, setProducts] = useState([]); const [categories, setCategories] = useState([]); useEffect(() => { const fetchProducts = async () => { const { result: categories } = await fetchCategories(); setCategories(categories); const { result } = await getCollectionProducts(categorySlug); setProducts(result); }; fetchProducts(); }, [categorySlug]); const itemList = products?.products; const aggregations = products?.aggregations; const categoryList = categories?.category; return ( <div class="min-h-screen mt-20 flex"> <div className="min-w-[20%] p-4 bg-white border-r"> {Array.isArray(aggregations) && aggregations?.map((aggregation, index) => aggregation?.attribute_code !== "price" ? ( <div key={index}> <h3 class="text-lg font-semibold mb-2">{aggregation?.label}</h3> <div class="mb-4"> {Array.isArray(aggregation?.options) && aggregation?.options?.map((option, ind) => ( <div class="flex items-center" key={ind}> <input type="checkbox" value={option.value} id={option?.label} class="mr-2" /> <label for={option?.label}>{option?.label}</label> </div> ))} </div> </div> ) : ( <div className="mb-2"> <h3 class="text-lg font-semibold mb-2">{aggregation?.label}</h3> <div class="flex flex-col justify-between items-center"> <div class="flex w-full justify-between"> <span>${aggregation?.options?.min_price}</span> <span>${aggregation?.options?.max_price}</span> </div> <input type="range" min={aggregation?.options?.min_price} max={aggregation?.options?.max_price} class="w-full mt-2 text-primary bg-primary" /> </div> </div> ) )} </div> <div class="p-6 max-w-[80%]"> <div class="flex justify-between items-center mb-4"> <div> <span class="text-sm text-gray-500"> Products / Default Category / Collections </span> </div> <div class="flex items-center"> <input type="text" placeholder="Search..." class="border rounded px-2 py-1 mr-4" /> <select class="border bg-gray-100 rounded px-2 py-1 mr-4"> <option>Public Pricelist</option> </select> <select class="border rounded px-2 bg-gray-100 py-1"> <option>Sort By: Featured</option> <option>Price: Low to High</option> <option>Price: High to Low</option> </select> </div> </div> <div class="flex mb-6 overflow-x-scroll py-3 space-x-2"> {Array.isArray(categoryList) && categoryList?.map((cat, index) => ( <Link to={`/shop/category/${cat.url_key}`} key={index} class="bg-gray-100 text-sm px-4 py-2 rounded" > {cat?.name} </Link> ))} </div> <div class="grid grid-cols-4 gap-4"> {itemList?.items && Array.isArray(itemList?.items) && itemList?.items?.map((product, index) => ( <div key={index} class="bg-white p-4 rounded shadow"> <img src={product?.thumbnail?.name} alt={product?.name} class="mb-4 w-fit h-60" /> <h4 class="font-semibold text-lg mb-2">{product?.name}</h4> <p class="text-green-600 font-bold"> ${product?.price_range.minimum_price?.regular_price?.value} </p> </div> ))} </div> </div> </div> ); }; export default Category;
Step 5: Component Integration
1. Update App.js: Import and return the Category
.js component in your App.js
.
import "./App.css"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import Layout from "./pages/Layout"; import NoPage from "./pages/NoPage"; import Category from "./pages/Shop/Category"; function App() { return ( <BrowserRouter> <Routes> <Route path="/" element={<Layout />}> <Route index element={<h1> Render home page </h1>} /> <Route path="shop/category/:categorySlug" element={<Category />} /> <Route path="*" element={<NoPage />} /> </Route> </Routes> </BrowserRouter> ); } export default App;
Step 6: Run your app
1. Start the development server: Run the following command to start your React application.
npm run start
2. Show in browser: open your browser and go to to see your category page in action.

Conclusion
You have successfully created a category page using React.js which fetches data from Odoo! This approach can be further enhanced with features such as sorting and filtering to improve user experience.
We will need Webkul Odoo REST APIs for headless development services with Webkul and you can also check out our Open Source headless e-commerce app Odoo React.
Thank you for reading this blog.
Happy coding!