This is a submission for the KendoReact Free Components Challenge.
What I Built
Earlier this year, a company called Epicure closed down their business and website. They were primarily focused on cooking products, but they also had over 3,000 recipes on their site which were no longer accessible. I built a NextJS static site using data scraped from the Internet Archive to resurrect Epicure's lost recipes. When I built this website, I originally created it using MUI components. For this challenge, I have created a branch of that project using KendoReact components instead.
Demo
Take a look at the KendoReact Challenge branch of epicure-recipes here:
https://deploy-preview-23--epicure-recipes.netlify.app/
And if you'd like, browse the source code here:
https://github.com/peiche/epicure-recipes/tree/kendo-challenge/
Here are a few screenshots of the website's different browsing capabilities. It is possible to view recipes in a list; recipes tagged under a category; and recipes tagged as using a particular Epicure product. This can vary widely, since Epicure made seasoning packets and jars, as well as kitchen utensils and tools like burger presses and microwave steamers.
When I was scraping data from Epicure's recipes, I found that each page contained data in JSON format that contained not only the recipe itself, but also category and product information. This made it quite easy for me to build out tag and product pages as well. I don't have any interest in upselling their products, of course. I'm not affiliated with Epicure, and they aren't manufacturing any new ones, anyway. But it is useful to know what products are used in a recipe, and if you have one of those products, the ability to find more recipes that use it.
Because this is a static site built from JSON data, there is no way to reorder recipes on any given page (although the metadata for dates published and the like does exist in the original JSON). These pages are good for browsing, but if you're looking for a specific recipe, you're better off using the search page.
The search is built using Algolia's InstantSearch for React. I mentioned that the site is built on static JSON data; that very same data was also used to generate the Algolia search index.
The site's search is separated into two parts. The first is the search page, which uses the aforementioned index. Since I already have data for tags and products, it required no extra effort on my part to include that data as facets.
The second part of the search is the suggest, which Algolia offers as a "learning" index. It uses the primary search index and updates it based on usage, adding and modifying records as people use the site.
KendoReact Experience
Below is a list of KendoReact components I used in the project, followed by some custom work when they couldn't offer enough functionality or customization for my liking.
Typography
This is a pretty obvious one, and almost shouldn't even count in the list of components. But it is one, since with this single component one can render a variety of typography-related tags.
Button
Buttons are used throughout the site, primarily for functionality that doesn't directly result in navigation to a different page. This includes things like the Search button in the navbar and pagination on the search page.
The difficulty I encountered with converting from MUI to KendoReact was that Kendo's Button component can't be a link. It can only render a <button>
element, never a <a>
. Sure, you can set the onClick
event to trigger navigation, but for SEO purposes I prefer to render a link. To that end, there are instances on the site where I manually created anchor elements that merely look like buttons using the same CSS classes as the Button component. You can see an example of this in the "button" on the homepage.
AppBar
The site's top navigation uses this, along with subsequent components. Rather than use display: flex
in custom CSS to create spacing, I was able to leverage the AppBarSection and AppBarSpacer components with zero utility class usage.
GridLayout and GridLayoutItem
In places where I needed to create a grid, such as the list pages and search results, I used these components. Unlike MUI's Grid counterpart, these use display: grid
rather than display: flex
. I am less familiar with CSS Grid than I am with Flex, but making a symmetrical layout is simple enough.
What I did have issues with is making the layout responsive. I found tutorials on how to do so with KendoReact (like this one) that require extensive configuration or creating a listener to window resizing. None of that appealed to me, so I created a couple utility classes in my stylesheet, one for a two-column layout (used on the search results page) and one for a thee-column layout (used on all the list grid pages).
Card
Inside every GridLayoutItem component was a shared recipe card component, which used Card and subsequent components, like CardImage, CardHeader, and CardBody. Some style specificity I had to override with the utility classes provided by the themes' stylesheets (more on that later).
TextBox
I don't have much by way of forms on the site, but there are two: the search suggest and the search results page itself.
Chip
You might think that I'm using the Chip component on the recipe detail page, but you'd be wrong. Those are actually a custom component with all the classes to recreate the appearance of a Chip. The reason I didn't use the KendoReact component is simple: you can't make it a link, only a button. For SEO purposes, I prefer to use a link when navigation is required.
On the search results page, however, I am using the Chip as provided by KendoReact. There's no navigation there: clicking on one of those will rearrange the search filters. The hit count next to each facet is also a Chip, but used solely for its display design.
Checkbox
On the search results page, each facet is represented by a checkbox. Checking it will filter according to that selection; unchecking it will undo the selection.
Dialog
To pop up the search suggest modal, I'm using the Dialog component. Super simple, and no customization required.
Drawer
Like every page, the search results page is responsive. I designed the entire site to be usable on a phone as well as a computer, and the search is no exception. When your screen is too small to display the search filters alongside the results, I hide the filters in a sidebar. Inside the drawer, the selected facets are also displayed.
I did find the Drawer somewhat counterintuitive to use, as the default behavior expects to be given a list of items as navigation. However, there is a component called DrawerNavigation
that can be used to manually insert any kind of content inside the drawer.
Switch
I put a Switch in the header to swap between light and dark modes (more on that in a minute). There isn't a whole lot of customization; it only allows for custom text in the different toggle states, and I really wanted to use sun and moon icons. I found a site that lets you search for symbols, so I copied those characters and put them in the text props.
The selection defaults to light mode, but switching it saves the change to local storage, allowing a user to keep using that theme without having to click it again the next time they visit.
Custom Components
As I mentioned, some of KendoReact's components did not offer enough versatility to use out-of-the-box. I want to talk about those for a minute here.
Button
I already mentioned this one, but I think it bears repeating. Sometimes you want to style a link like a button, and KendoReact does not provide an easy way to do that. Every time I wanted a link that looked like a button, I would create the button, copy its CSS classes, and then manually create the link. (In my case, I used NextJS's Link component.)
{/* Button component */}
<Button
themeColor='primary'
fillMode='solid'
>Print</Button>
{/* Recreated link */}
<Link
className='k-button k-button-md k-button-solid k-button-solid-primary k-rounded-md'
href={`/recipe/${slug}/print`}
target='_blank'
>
<span className='k-button-text'>Print</span>
</Link>
Chip
The "chips" on the recipe detail page, as I've mentioned, are custom anchor elements. Much like I did with the links that look like buttons, I created a Chip, copied its CSS classes, and created an anchor element with those classes. You can see the custom shared component here:
https://github.com/peiche/epicure-recipes/blob/kendo-challenge/src/components/chipLink.tsx
Pager
As far as I could tell from the documentation, the Pager component was to be used within the context of a data table. I had two use-cases: pagination for the browse pages and the search results page. The former had to result in navigation, so they had to be links. The latter are buttons since they update the page but don't result in navigation away from a page. (The URL does get updated, though, so a specific search result can be linked to.)
I ended up writing my own pagination components, one for each use-case. I briefly considered writing a single component that could handle both, but there is such a thing as over-engineering! 😆 As a result, there are two components, each with identical logic to skip over displaying every single page.
https://github.com/peiche/epicure-recipes/blob/kendo-challenge/src/components/pagination.tsx
https://github.com/peiche/epicure-recipes/blob/kendo-challenge/src/components/searchPagination.tsx
Breadcrumb
The breadcrumb component was another case where the elements could not be given a destination outside of an onClick
event. It made even less sense here because each leaf in the breadcrumb's path is an anchor element, but is hard-coded to have "#" as the href value. In order to have a breadcrumb component that rendered functional anchor elements, I wrote my own, using the existing CSS classes used by the Kendo component.
https://github.com/peiche/epicure-recipes/blob/kendo-challenge/src/components/breadcrumbs.tsx
Delightfully Designed
I created an account on Telerik and signed up for the seven-day trial of the Theme Builder pro, just so I could have more than one design. (The free tier has a limit of one.) I created two color themes: one light and one dark.
I exported the two themes, but my use of the downloads changed over the course of development. I originally used the Sass exports, but I found that the variables used there were Sass variables, not custom properties (i.e., native CSS variables), so I switched to the CSS exports instead. (My separate global stylesheet still uses Sass.)
I initially put the stylesheets inside /src/themes
, but moved it into /public
because the CSS theme switcher dependency I was using required a static, public URL.
Final Notes
I did mention using some utility classes to override some default styles. Before I did a deep dive into the styles included with the theme -- both the base stylesheet and the exported files from Theme Builder -- I considered using Tailwind to supplement what I needed. After some digging, however, I found that Kendo already provided most of what I needed. (I did, as I mentioned, have to create a couple grid-related utility classes.)
I also want to note that while I am using a few icons, none of them are Kendo's. I did start out using them, but there are some they don't have, like the fork and knife icon I'm using on the recipe detail page. I used Font Awesome for that. Rather than have visual inconsistency (heaven forbid!) I decided to make all the icons come from Font Awesome.
The best hobby web apps are the ones you build for yourself. That's no less true in this case, since I use it quite a bit. ("I'm not just the president, I'm also a client!") It's invaluable to me to be able to pull up a recipe on my phone; to that end, the entire site is responsive: from the home page and browse to the search and recipe detail pages. Maybe you can have a use for it too!
Thanks for reading!
I've had a lot of fun (and some frustration) in refactoring my existing site to use a new set of components. And if you're a foodie, feel free to poke around my recipe site. It's a labor of love.
I may still write about the process I used to generate the JSON files which form the base of the static site, but that is a story for another time.
Top comments (1)
so tasty recipes,
hope namine is doing well