First Steps
- Your product manager (at least) needs to have a Sanity user account with email and password.
Sample AirBnb Property Rental Clone
Document Schemas (schemas/*.js)
property.js
export default {
name: "property",
title: "Property",
type: "document",
fields: [
{
name: "title",
title: "Title",
type: "string",
},
{
name: "location",
title: "Location",
type: "geopoint",
},
{
name: "propertyType",
title: "Property Type",
type: "string",
options: {
list: [
{
title: "House",
value: "HOUSE",
},
{
title: "Flat",
value: "FLAT",
},
{
title: "Bed and Breakfast",
value: "BED_AND_BREAKFAST",
},
{
title: "Boutique Hotel",
value: "BOUTIQUE_HOTEL",
},
],
layout: "radio",
},
},
{
name: "mainImage",
title: "Main Image",
type: "image",
options: {
hotspot: true,
},
},
{
name: "images",
title: "Images",
type: "array",
of: [{ type: "propertyImage" }],
},
{
name: "pricePerNight",
title: "Price per Night",
type: "number",
},
{
name: "beds",
title: "Beds",
type: "number",
},
{
name: "bedrooms",
title: "Bedrooms",
type: "number",
},
{
name: "slug",
title: "Slug",
type: "slug",
options: {
source: "title",
maxLength: 100,
},
},
{
name: "id",
title: "ID",
type: "number",
},
{
name: "description",
title: "Description",
type: "string",
},
],
};
Now import the document schemas into schemas/schema.js
:
// First, we must import the schema creator
import createSchema from "part:@sanity/base/schema-creator";
// Then import schema types from any plugins that might expose them
import schemaTypes from "all:part:@sanity/base/schema-type";
// We import object and document schema
import property from "./property";
import propertyImage from "./propertyImage";
// Then we give our schema to the builder and provide the result to Sanity
export default createSchema({
// We name our schema
name: "default",
// Then proceed to concatenate our document type
// to the ones provided by any plugins that are installed
types: schemaTypes.concat([
/* Your types here! */
property,
propertyImage,
]),
});
Next.js Frontend
Create Next.js app:
yarn create next-app --typescript lovia-pwa-next
Add next-sanity
dependency:
yarn add next-sanity
Add /sanity.ts
that we’ll use to query Sanity:
import {
ClientConfig,
createClient,
createImageUrlBuilder,
} from "next-sanity";
const config: ClientConfig = {
apiVersion: "2021-09-05",
/**
* Find your project ID and dataset in `sanity.json` in your studio project.
* These are considered “public”, but you can use environment variables
* if you want differ between local dev and production.
*
* https://nextjs.org/docs/basic-features/environment-variables
**/
dataset: process.env.NEXT_PUBLIC_SANITY_DATASET || "production",
projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID!,
/**
* Set useCdn to `false` if your application require the freshest possible
* data always (potentially slightly slower and a bit more expensive).
* Authenticated request (like preview) will always bypass the CDN
**/
useCdn: process.env.NODE_ENV === "production",
};
/**
* Set up a helper function for generating Image URLs with only the asset reference data in your documents.
* Read more: https://www.sanity.io/docs/image-url
**/
export const urlFor = (source: string) =>
createImageUrlBuilder(config).image(source);
// Set up the client for fetching data in the getProps page functions
export const sanityClient = createClient(config);
Configure the app in /.env
file:
NEXT_PUBLIC_SANITY_PROJECT_ID=.... # change this
NEXT_PUBLIC_SANITY_DATASET=production
Create /src folder. Then, move /pages folder into /src.
Now you can fetch the document/content in /src/pages/index.tsx
:
import type { GetServerSideProps, NextPage } from "next";
import Head from "next/head";
import Image from "next/image";
import { sanityClient } from "../sanity";
const Home: NextPage<{ schools: {name: string}[] }> = ({ schools }) => {
console.debug('schools=', schools);
return <div>
{schools.map(school => (
<li key={school.name}>{school.name}</li>
))}
</div>;
};
export const getServerSideProps: GetServerSideProps = async () => {
const query = '*[ _type == "school" ]';
const schools = (await sanityClient.fetch(query)) || [];
return {
props: {
schools
}
};
}
export default Home;
React UI Libraries / Frameworks
Choices:
- Material UI
TypeScript support.
v5 now uses Emotion for component styling.
Figma Kit ($69)
Data Grid + Date Range Picker bundle is $186 - Ant Design & Ant Design Pro
TypeScript by default.
Medihack’s opinion: “The design of Ant is really polished. In my opinion the cleanest design of them all (but I also find Material Design absolutely space-wasting). But under the hood, there are some flaws of Ant Design. The styling support by using LESS is in my view quite antiquated and there seems to be no soon solution (nearly all Github issues in this direction were closed or came to a halt). Also, the whole library is so opinionated (global CSS styles, fixed sizes in “px”) that it is a pain if you want to customize it. On the other side, if you look at the many themes of PrimeReact it seems to be very well customizable.”
Pluses:
(+) so many components and all are free
(+) in addition to Ant Design components, there are ProComponents like ProLayout, ProTable, which are very complex and they’re free
Minuses:
(-) uses LESS instead of Scss
(-) does not use Emotion or similar
(-) not working well with Next.js
(*) promotes UmiJS instead of Next.js, which can be good or bad depending on where you stand
(-) does not work well with Storybook, instead it promotes Dumi
(-) uses Global CSS/LESS, so you can’t integrate/embed it inside another app (e.g. WordPress) - PrimeReact (2.2k stars) + PrimeFlex + PrimeBlocks (Free Blocks)
TypeScript supported as type definitions.
Drag and Drop OrderList is included in Free version.
Drag and Drop Tree is included in Free version.
PrimeBlocks Pro: $99 including Figma UI Kit
Free template: Sigma
Premium templates: Freya ($59), Diamond ($59), Avalon Bootstrap-like ($49), Ultima ($79, most complete one), Serenity ($49), Apollo ($49), Babylon ($49), Roma ($39)
Free Figma UI Kit but does not include component features.
Medihack’s opinion: “Companies like PrimeReact (focused on the UI library itself) or independent projects build the components for many use cases and are more open for external suggestions (just my experience).”
Form validation support: Formik, React Hook Form, React Final Form
Does not use Emotion, but uses Sass. PrimeFlex is a CSS utility library (no JS-in-CSS at all).
Prime UI and PrimeFlex uses SCSS variables and free version only gets CSS, but cannot customize SCSS. This means that customizing colors dynamically at runtime both requires a paid license and still challenging (due to need to generate from SCSS at build time). - Chakra UI (21k stars).
Chakra UI Pro: Marketing+Application $249 including Figma UI Kit.
Has dark theme.
Does not provide admin / app template (even commercial ones?).
Free List cannot drag-and-drop, requires Drag and Drop List in Pro.
Medihack’s choice if not for legacy IE11 support: “After a long decision, I took Material UI. Chakra UI would be an even better fit, but I need IE11 support (healthcare :-((( The thing is that both libraries are with their design systems much better customizable. It’s not just the CSS in JS solution. It’s the whole theming thing around it. A special example ist Ant Design. It looks so nice in it’s default but is so crappy under the hood (LESS styles, everything is fixated in px, global CSS). But Baseweb is also a good choice I guess. It has the same principles as Material UI and Chakra.”
Backedleaf opinion: “We went from Material (v1) to Ant (v2) to chakra (v3) and every move has felt like the right one. Chakra is missing a few core components like powerful tables and date pickers. If you’re willing to roll your own like we were it’s super flexible, otherwise choose Ant. I haven’t used Material since we moved away from it but man it was absolutely miserable to use.”
Comparison Chakra UI vs Ant Design from Chakra’s POV.
Chakra Templates. - Base Web (6.8k stars).
Drag and Drop List is included in Free version.
No Drag and Drop Tree.
Medihack’s opinion: “Yes, looks also very nice. I like the “raw” style. But I would miss some components (switches, autosizing text areas, tree selects, …). The problem in my opinion with Open Source UI frameworks of many big companies is that these are very focused on their own business (which is of course ok) and the UI components they need.” - React Bootstrap
Most used by Envato themes. - Blueprint (18k stars)
Has dark theme.
No Drag and Drop List. Not even a List? - Ionic
Ionic v6 uses Stencil and Web Components.
Pros:
(+) Ionic components looks most native to both Android and iOS
Cons:
(-) Component library is not as extensive as Ant Design especially Ant Design Pro
(-) Since it is “too native”, testing Ionic visually is more challenging as the colors, layout etc. will look different depending on platform
References:
- https://www.reddit.com/r/reactjs/comments/lco24w/why_is_primereact_such_an_underdog/
- https://www.reddit.com/r/reactjs/comments/mpkea6/chakra_ui_vs_material_ui/
Using PrimeReact
PrimeReact will give us better design than plain HTML.
yarn add primereact primeicons primeflex react-transition-group
Adding Ant ProLayout and Ant Design
Ant ProLayout and Ant Design will give us better design.
Issue: Due to Next.js issue #11584, we can’t just install antd
normally, but unfortunately must use next-plugin-antd-less instead:
yarn add next-plugin-antd-less antd
yarn add --dev babel-plugin-import babel-plugin-module-resolver
Create /babel.config.js
:
// eslint-disable-next-line func-names
module.exports = function (api) {
api.cache(true);
return {
presets: [['next/babel']],
plugins: [
['import', { libraryName: 'antd', style: true }],
// @-style module resolution
['module-resolver', { alias: { '@': './src' } }],
// ['inline-react-svg'],
// ['add-react-displayname'],
],
};
};
Configure @-style module resolution in /tsconfig.json
:
...
"jsx": "preserve",
"paths": {
"@/*": [
"./src/*"
]
}
},
...
Configure /next.config.js:
const withAntdLess = require("next-plugin-antd-less");
/** @type {import('next').NextConfig} */
module.exports = withAntdLess({
// optional
// modifyVars: { '@primary-color': '#04f' },
// optional
lessVarsFilePath: "./src/styles/variables.less",
// optional
lessVarsFilePathAppendToEndOfContent: false,
// optional https://github.com/webpack-contrib/css-loader#object
cssLoaderOptions: {},
// Other Config Here...
webpack(config) {
return config;
},
reactStrictMode: true,
});
yarn add @ant-design/pro-layout