{"id":4112,"date":"2020-07-07T12:31:30","date_gmt":"2020-07-07T07:01:30","guid":{"rendered":"https:\/\/razorpay.com\/blog\/?p=4112"},"modified":"2025-03-07T14:37:35","modified_gmt":"2025-03-07T09:07:35","slug":"developer-experience-while-building-react-components","status":"publish","type":"post","link":"https:\/\/razorpay.com\/blog\/developer-experience-while-building-react-components\/","title":{"rendered":"Developer Experience While Building React Components"},"content":{"rendered":"<p data-pm-slice=\"1 1 []\">Managing consistency across applications and teams is always a challenge, especially in a large firm. Be it UX, design, code-styling, build tools, the list goes on. This is what largely constitutes the Developer Experience.<\/p>\n<p>A good Developer Experience (DX) is when a developer can get their work done in an optimised manner, without being worried about things like bootstrapping an application, indentation standards, naming conventions..<\/p>\n<p>Talking about the React ecosystem, it has taken care of the DX from the very beginning by its component style pattern, further increasing the DX by building tooling like the <code>create-react-app<\/code>, etc. Then there are design systems like <a href=\"https:\/\/github.com\/auth0\/cosmos\" rel=\"noopener noreferrer nofollow\" target=\"_blank\">cosmos<\/a> that bring consistency to the way we make applications across different repositories using the same react components.<\/p>\n<p>In this blog we will look at how to build a reusable notification system and avoid the common pitfalls we would have picked up while ramping on React. Consider a notification\/alert\/toast popup component, as a component, it should have the ability to render whatever child components are passed to it and it should be able to close\/hide itself on click of the close button (or even close or hide itself after a set timeout). In the simplest of designs the engineer would use a prop drilling pattern and pass an onClose function to the toast component which would be able to toggle the state in the parent component that hosts our notifications.<\/p>\n<p>This, by design, is not wrong. However, from a developer experience perspective, why should the parent component host the function that would be responsible for hiding\/closing the notification. This responsibility should be with the component itself. What makes the react-notifier highly reusable is the fact that any other component using it does not have to worry about the state (hide\/show or open\/close) of the notification component, rather it exposes an add and remove method that takes care of the states for you.<\/p>\n<p>This traditionally is possible managing a global state using redux. However, in the spirit of embracing the latest react feature we would be using react hooks and the context API to achieve the same. Excited enough? Let&#8217;s jump in!!<\/p>\n<h2>Building a reusable notification system with react hooks and context API<\/h2>\n<p>The notification system is built with React and no external library. The toast notifications will be stackable, meaning we can have multiple notifications showing up at the same time. These will be capable of rendering a string or another react component within itself.<\/p>\n<h3>Background<\/h3>\n<p>The following assumes that the reader has a thorough understanding of React and React hooks. For a detailed understanding of React-hooks please refer the <a href=\"https:\/\/reactjs.org\/docs\/hooks-intro.html\" rel=\"noopener noreferrer nofollow\" target=\"_blank\">react hooks docs<\/a>.<\/p>\n<p>We will be using the following hooks<\/p>\n<ul>\n<li><code>useState<\/code>, this allows us to use the react state within functional components (this earlier used to be possible only in class-based components and functional components were used only as presentational components)<\/li>\n<li><code>useContext<\/code>, this hook takes a context object as an input and returns the value passed in <code>Context.Provider<\/code>. React context API provides a way to pass the props\/data in a component tree without having to pass the props\/data to every child at every level (prop drilling)<\/li>\n<\/ul>\n<p>Below is the syntax for the context API for reference<\/p>\n<div class=\"code-block\">\n<pre><code>const SampleContext = React.createContext(\/*initialVAlue*\/);\r\n\/\/ wrap the parent component with the context provider\r\n&lt;SampleContext.Provider value={\/*value*\/}&gt;\r\n  .\r\n  .\r\n  .\r\n  .\r\n  \/* n level child can access the provider value using useContext() *\/\r\n  const context = React.useContext(SampleContext)\r\n  &lt;SomeComponent&gt;\r\n    &lt;Button onClick={context.someMethod}\/&gt;\r\n  &lt;\/SomeComponent&gt;\r\n&lt;\/SampleContext.Provider&gt;<\/code><\/pre>\n<\/div>\n<ul>\n<li><code>useReducer<\/code>, this is a custom hook baked into react hooks, which provides a redux reducer like interface. The reducer takes an initial state and action object having type and a payload, based on the type the initial state is recreated (pure function) and returned. A dispatch function is used to trigger the reducer switch.<\/li>\n<\/ul>\n<p>The usage of <code>useReducer<\/code> is copied from the react docs.<\/p>\n<div class=\"code-block\">\n<pre><code>\/\/ the reducer function that provides new state based on action.type\r\nfunction todosReducer(state, action) {\r\n  switch (action.type) {\r\n    case 'add':\r\n      return [\r\n        ...state,\r\n        {\r\n          text: action.text,\r\n          completed: false\r\n        }\r\n      ];\r\n    \/\/ ... other actions ...\r\n    default:\r\n      return state;\r\n  }\r\n}\r\n\r\n\/\/ the useReducer function keeps track of the state and returns the new state and a dispatcher function.\r\nfunction useReducer(reducer, initialState) {\r\n  const [state, setState] = useState(initialState);\r\n\r\n  function dispatch(action) {\r\n    const nextState = reducer(state, action);\r\n    setState(nextState);\r\n  }\r\n\r\n  return [state, dispatch];\r\n}\r\n\r\n\/\/ Sample usage of the useReducer.\r\nfunction Todos() {\r\n  const [todos, dispatch] = useReducer(todosReducer, []);\r\n\r\n  function handleAddClick(text) {\r\n    dispatch({ type: 'add', text });\r\n  }\r\n\r\n  \/\/ ...\r\n}<\/code><\/pre>\n<\/div>\n<h3>Lets build<\/h3>\n<blockquote><p>Note: we will be using create-react-app to scaffold a basic react app, also please install the latest stable version of NodeJS.<\/p><\/blockquote>\n<p>Create a basic react app using the <code>create-react-app<\/code>.<\/p>\n<div class=\"code-block\">\n<pre><code>$: npx create-react-app react-notifier\r\n$: cd react-notifier\r\n$: npm run start # this will start a development server at http:\/\/localhost:3000\/<\/code><\/pre>\n<\/div>\n<p>Now open the created project in your favourite code editor, and edit <code>src\/App.js<\/code> to have<\/p>\n<div class=\"code-block\">\n<pre><code>\/\/ src\/App.js\r\nimport React from 'react';\r\nimport '.\/App.css';\r\n\r\nfunction App() {\r\n  return &lt;div className=\"App\"&gt;Hello&lt;\/div&gt;;\r\n}\r\n\r\nexport default App;<\/code><\/pre>\n<\/div>\n<p>Also edit <code>src\/App.css<\/code> to have the below code.<\/p>\n<div class=\"code-block\">\n<pre><code>.App {\r\n  text-align: left;\r\n}<\/code><\/pre>\n<\/div>\n<p>Next create a folder structure as below:<\/p>\n<div class=\"image\"><img decoding=\"async\" src=\"https:\/\/s3-ap-southeast-1.amazonaws.com\/rzp-prod-outline-wiki\/uploads\/ea446fe4-aa8a-471c-aea4-294bd22c3258\/636db588-eb32-4469-a952-8d3dd5efee80\/folder_structure.png\" \/><\/div>\n<p>We call our notification component Toast.<\/p>\n<h3>Lets create the Toast Component<\/h3>\n<p>This will be a simple component that takes an array and renders the same based on whether the element of the array is a function or an object<\/p>\n<div class=\"code-block\">\n<pre><code>\/\/ src\/components\/Toast\r\n\r\nimport React from 'react';\r\n\r\nexport default function Toast({ toast }) {\r\n  \/\/ function to decide how to render the content of the toast\r\n  function renderItem(content) {\r\n    if (typeof content === 'function') {\r\n      return content();\r\n    } else {\r\n      return &lt;pre&gt;{JSON.stringify(content, null, 2)}&lt;\/pre&gt;;\r\n    }\r\n  }\r\n  return (\r\n    &lt;div className=\"toast\"&gt;\r\n      &lt;div className=\"toast-container\"&gt;\r\n        {\/* Displaying each element of the toast *\/}\r\n        {toast.map(t =&gt; {\r\n          return (\r\n            &lt;div\r\n              className={`toast-container-item ${t.type ? t.type : ''}`}\r\n              key={t.id}\r\n            &gt;\r\n              &lt;span role=\"img\" aria-label=\"close toast\" className=\"toast-close\"&gt;\r\n                &amp;times;\r\n              &lt;\/span&gt;\r\n              {renderItem(t.content)}\r\n            &lt;\/div&gt;\r\n          );\r\n        })}\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n}<\/code><\/pre>\n<\/div>\n<p>we will be using <code>.scss<\/code> for defining the CSS<\/p>\n<blockquote><p>Note: Please run <code>npm install --save node-sass<\/code> to compile <code>.scss<\/code> files<\/p><\/blockquote>\n<div class=\"code-block\">\n<pre><code>\/\/ styles\/base.scss\r\n\/\/ base colors\r\n$black: #212121;\r\n$white: #fff;\r\n$gray: #e0e0e0;\r\n$primaryBlue: #1652f0;\r\n$hoverBlue: #154de0;\r\n$red: #d9605a;\r\n\/\/ fonts\r\n$code: 'Oxygen Mono', monospace;\r\n\r\n\/\/ styles\/toast.scss\r\n@import '.\/base.scss';\r\n.toast {\r\n  position: fixed;\r\n  top: 50px;\r\n  right: 10px;\r\n  width: 300px;\r\n  max-height: 90vh;\r\n  overflow-y: scroll;\r\n  font-family: $code;\r\n  .toast-container {\r\n    display: flex;\r\n    flex-direction: column;\r\n    align-items: flex-start;\r\n    .toast-container-item {\r\n      border: $primaryBlue solid 1px;\r\n      margin: 5px 0px;\r\n      padding: 2px;\r\n      border-radius: 4px;\r\n      width: 100%;\r\n      min-height: 100px;\r\n      word-wrap: break-word;\r\n      background-color: $black;\r\n      box-shadow: 4px 4px 15px 2px rgba(black, 0.75);\r\n      color: $white;\r\n      transition: 0.2s;\r\n      &amp;:not(:first-child) {\r\n        margin-top: -3rem;\r\n      }\r\n      \/\/ &amp;:hover,\r\n      \/\/ &amp;:focus-within {\r\n      \/\/   transform: translateX(-2rem);\r\n      \/\/ }\r\n      &amp;:hover ~ .toast-container-item,\r\n      &amp;:focus-within ~ .toast-container-item {\r\n        transform: translateY(3rem);\r\n      }\r\n\r\n      &amp;.info {\r\n        border: $primaryBlue solid 1px;\r\n        background-color: $hoverBlue;\r\n      }\r\n      &amp;.danger {\r\n        border: $red solid 1px;\r\n        background-color: $red;\r\n      }\r\n      .toast-close {\r\n        cursor: pointer;\r\n        position: relative;\r\n        top: 5px;\r\n        font-size: 20px;\r\n        font-weight: 800;\r\n      }\r\n    }\r\n  }\r\n}<\/code><\/pre>\n<\/div>\n<p>We use <code>position: fixed;<\/code> along with the top and right attributes to have the toast notification appear from the top-right corner of the screen.<\/p>\n<p>Subsequently, we use the <code>display: flex;<\/code> property in the <code>toast-container<\/code>, to have a flexible layout<\/p>\n<p>To know more on flex please refer: <a href=\"https:\/\/css-tricks.com\/snippets\/css\/a-guide-to-flexbox\/\" rel=\"noopener noreferrer nofollow\" target=\"_blank\">A complete guide to flexbox<\/a><\/p>\n<p>Next, let us define our <code>ToastContext<\/code> so that we can trigger the component from anywhere in the application<\/p>\n<div class=\"code-block\">\n<pre><code>\/\/ contexts\/ToastContext.js\r\n\r\nimport React, { createContext, useReducer, useContext } from 'react';\r\nimport { createPortal } from 'react-dom';\r\nimport Toast from '..\/components\/Toast';\r\nimport '..\/styles\/toast.scss';\r\n\r\nexport const ToastContext = createContext();\r\n\r\nconst initialState = [];\r\n\r\nexport const ADD = 'ADD';\r\nexport const REMOVE = 'REMOVE';\r\nexport const REMOVE_ALL = 'REMOVE_ALL';\r\n\r\nexport const toastReducer = (state, action) =&gt; {\r\n  switch (action.type) {\r\n    case ADD:\r\n      return [\r\n        ...state,\r\n        {\r\n          id: +new Date(),\r\n          content: action.payload.content,\r\n          type: action.payload.type\r\n        }\r\n      ];\r\n    case REMOVE:\r\n      return state.filter(t =&gt; t.id !== action.payload.id);\r\n    case REMOVE_ALL:\r\n      return initialState;\r\n    default:\r\n      return state;\r\n  }\r\n};\r\n\r\nexport const ToastProvider = props =&gt; {\r\n  const [toast, toastDispatch] = useReducer(toastReducer, initialState);\r\n  const toastData = { toast, toastDispatch };\r\n  return (\r\n    &lt;ToastContext.Provider value={toastData}&gt;\r\n      {props.children}\r\n\r\n      {createPortal(&lt;Toast toast={toast} \/&gt;, document.body)}\r\n    &lt;\/ToastContext.Provider&gt;\r\n  );\r\n};\r\n\r\nexport const useToastContext = () =&gt; {\r\n  const ToastContext = React.useContext(ToastContext);\r\n  if (ToastContext === undefined) {\r\n    throw new Error('useToastContext must be used within a ToastConextProvider');\r\n  }\r\n  return ToastContext;\r\n};<\/code><\/pre>\n<\/div>\n<p>Let&#8217;s break down the above code.<\/p>\n<p>We initialise an empty react context using <code>React.createContext();<\/code>, next we prepare the actions that would be required for the notification system, these can be put in separate files if the application becomes bigger and has a lot of actions (to remove conflicting actions),<\/p>\n<div class=\"code-block\">\n<pre><code>export const ADD = 'ADD';\r\nexport const REMOVE = 'REMOVE';\r\nexport const REMOVE_ALL = 'REMOVE_ALL';<\/code><\/pre>\n<\/div>\n<p>Next is the reducer function that takes the initial state as an empty array and based on the action.type pushes to the array or removes while returning a new state.<\/p>\n<p>We also provide an ID to all new entries in toast array, which makes it easier to remove the said target toast\/notification.<\/p>\n<p>We then create a Provider function that provides the value to the empty context created via, <code>&lt;Context.Provider&gt;<\/code> We combine the returned newState and the dispatcher function from the useReducer hook and send these as values via context API.<\/p>\n<p>We use the <code>React.createPortal<\/code> to render the toast component in the document.body, this provides easier\/less conflicting styling and document flow.<\/p>\n<p>Lastly, we expose the useContext (an easier to use version of <code>&lt;Context.Consumer&gt;<\/code>) hook via a custom hook.<\/p>\n<p>Update the toast component to use the <code>useToastContext<\/code> hook so that it can have its own dispatcher to close the toast\/notification from within the component<\/p>\n<div class=\"code-block\">\n<pre><code>\/\/ src\/components\/Toast.js\r\nimport React from 'react';\r\n\r\nimport { useToastContext, REMOVE } from '..\/contexts\/ToastContext';\r\n\r\nexport default function Toast({ toast }) {\r\n  const { toastDispatch } = useToastContext();\r\n  function renderItem(content) {\r\n    if (typeof content === 'function') {\r\n      return content();\r\n    } else {\r\n      return &lt;pre&gt;{JSON.stringify(content, null, 2)}&lt;\/pre&gt;;\r\n    }\r\n  }\r\n  return (\r\n    &lt;div className=\"toast\"&gt;\r\n      &lt;div className=\"toast-container\"&gt;\r\n        {toast.map(t =&gt; {\r\n          return (\r\n            &lt;div\r\n              className={`toast-container-item ${t.type ? t.type : ''}`}\r\n              key={t.id}\r\n            &gt;\r\n              &lt;span\r\n                role=\"img\"\r\n                aria-label=\"close toast\"\r\n                className=\"toast-close\"\r\n                onClick={() =&gt;\r\n                  toastDispatch({ type: REMOVE, payload: { id: t.id } })\r\n                }\r\n              &gt;\r\n                &amp;times;\r\n              &lt;\/span&gt;\r\n              {renderItem(t.content)}\r\n            &lt;\/div&gt;\r\n          );\r\n        })}\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n}<\/code><\/pre>\n<\/div>\n<p>To see the above in action, let&#8217;s make some basic routes and navigation using the <code>react-router-dom<\/code>.<\/p>\n<div class=\"code-block\">\n<pre><code>$: npm install -s react-router-dom<\/code><\/pre>\n<\/div>\n<p>Since the following will be made only to show the usage of the Toast Component, we will be defining the components for each route within <code>src\/App.js<\/code> file.<\/p>\n<h3>Defining the home component<\/h3>\n<div class=\"code-block\">\n<pre><code>export const Home = () =&gt; {\r\n  const { toastDispatch } = useToastContext();\r\n  return (\r\n    &lt;div&gt;\r\n      &lt;button\r\n        onClick={() =&gt;\r\n          toastDispatch({\r\n            type: ADD,\r\n            payload: {\r\n              content: { sucess: 'OK', message: 'Hello World' }\r\n            }\r\n          })\r\n        }\r\n      &gt;\r\n        Show basic notification\r\n      &lt;\/button&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n};<\/code><\/pre>\n<\/div>\n<p>The above is a simple component that renders a button, the onClick of the button dispatches an action with <code>type: ADD<\/code> some content and optionally a type of <code>info<\/code> or <code>danger.<\/code> This is used to render the background colour of the toast\/notification.<\/p>\n<p>Similarly, we will define some other components just to show various types of toast components use cases.<\/p>\n<p>The final <code>scr\/App.js<\/code> file is below<\/p>\n<div class=\"code-block\">\n<pre><code>import React from 'react';\r\nimport { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';\r\nimport '.\/App.css';\r\nimport { useToastContext, ADD, REMOVE_ALL } from '.\/contexts\/ToastContext';\r\n\r\nexport const Home = () =&gt; {\r\n  const { toastDispatch } = useToastContext();\r\n  return (\r\n    &lt;div&gt;\r\n      &lt;button\r\n        onClick={() =&gt;\r\n          toastDispatch({\r\n            type: ADD,\r\n            payload: {\r\n              content: { sucess: 'OK', message: 'Hello World' }\r\n            }\r\n          })\r\n        }\r\n      &gt;\r\n        Show basic notification\r\n      &lt;\/button&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n};\r\nexport const Info = () =&gt; {\r\n  const { toastDispatch } = useToastContext();\r\n  return (\r\n    &lt;div&gt;\r\n      &lt;button\r\n        onClick={() =&gt;\r\n          toastDispatch({\r\n            type: ADD,\r\n            payload: {\r\n              content: { sucess: 'OK', message: 'Info message' },\r\n              type: 'info'\r\n            }\r\n          })\r\n        }\r\n      &gt;\r\n        Show Info notification\r\n      &lt;\/button&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n};\r\n\r\nexport const Danger = () =&gt; {\r\n  const { toastDispatch } = useToastContext();\r\n  return (\r\n    &lt;div&gt;\r\n      &lt;button\r\n        onClick={() =&gt;\r\n          toastDispatch({\r\n            type: ADD,\r\n            payload: {\r\n              content: { sucess: 'FAIL', message: 'Something nasty!' },\r\n              type: 'danger'\r\n            }\r\n          })\r\n        }\r\n      &gt;\r\n        Show danger notification\r\n      &lt;\/button&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n};\r\n\r\nexport const CutomHTML = () =&gt; {\r\n  const { toastDispatch } = useToastContext();\r\n  return (\r\n    &lt;div&gt;\r\n      &lt;button\r\n        onClick={() =&gt;\r\n          toastDispatch({\r\n            type: ADD,\r\n            payload: {\r\n              content: () =&gt; {\r\n                return (\r\n                  &lt;div&gt;\r\n                    &lt;h4&gt;Error&lt;\/h4&gt;\r\n                    &lt;p&gt;Something nasty happened!!&lt;\/p&gt;\r\n                  &lt;\/div&gt;\r\n                );\r\n              },\r\n              type: 'danger'\r\n            }\r\n          })\r\n        }\r\n      &gt;\r\n        Show danger notification with custom HTML\r\n      &lt;\/button&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n};\r\n\r\nexport default function App() {\r\n  const { toast, toastDispatch } = useToastContext();\r\n  function showClearAll() {\r\n    if (toast.length) {\r\n      return (\r\n        &lt;button\r\n          onClick={() =&gt;\r\n            toastDispatch({\r\n              type: REMOVE_ALL\r\n            })\r\n          }\r\n        &gt;\r\n          Clear all notifications\r\n        &lt;\/button&gt;\r\n      );\r\n    }\r\n  }\r\n  return (\r\n    &lt;div className=\"App\"&gt;\r\n      &lt;Router&gt;\r\n        &lt;ul&gt;\r\n          &lt;li&gt;\r\n            &lt;Link to=\"\/\"&gt;Home&lt;\/Link&gt;\r\n          &lt;\/li&gt;\r\n          &lt;li&gt;\r\n            &lt;Link to=\"\/info\"&gt;Info&lt;\/Link&gt;\r\n          &lt;\/li&gt;\r\n          &lt;li&gt;\r\n            &lt;Link to=\"\/danger\"&gt;Danger&lt;\/Link&gt;\r\n          &lt;\/li&gt;\r\n          &lt;li&gt;\r\n            &lt;Link to=\"\/custom-html\"&gt;Custom HTML&lt;\/Link&gt;\r\n          &lt;\/li&gt;\r\n        &lt;\/ul&gt;\r\n        &lt;Switch&gt;\r\n          &lt;Route exact path=\"\/\"&gt;\r\n            &lt;Home \/&gt;\r\n          &lt;\/Route&gt;\r\n          &lt;Route exact path=\"\/info\"&gt;\r\n            &lt;Info \/&gt;\r\n          &lt;\/Route&gt;\r\n          &lt;Route exact path=\"\/danger\"&gt;\r\n            &lt;Danger \/&gt;\r\n          &lt;\/Route&gt;\r\n          &lt;Route exact path=\"\/custom-html\"&gt;\r\n            &lt;CutomHTML \/&gt;\r\n          &lt;\/Route&gt;\r\n        &lt;\/Switch&gt;\r\n      &lt;\/Router&gt;\r\n      &lt;br \/&gt;\r\n      {showClearAll()}\r\n    &lt;\/div&gt;\r\n  );\r\n}<\/code><\/pre>\n<\/div>\n<p>A working demo of the above can be found at the following <a href=\"https:\/\/react-notifier.kevjose.now.sh\/\" rel=\"noopener noreferrer nofollow\" target=\"_blank\">link.<\/a><\/p>\n<h2>Wrapping up<\/h2>\n<p>To sum up, think of the features of a component that can be re-used and embed those to the component itself, rather than waiting for individual parent components to pass their own versions of this common functionality.<\/p>\n<p>The above is one of the several ways to achieve a better DX while building components for your application or even your own design system. This plays a long way in you building scalable applications that your co-developers would love to work with.<\/p>\n<p>Cheers!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-20260 size-full\" src=\"https:\/\/d6xcmfyh68wv8.cloudfront.net\/blog-content\/uploads\/2019\/11\/Banner-Image-4.png\" alt=\"Razorpay Partner Program\" width=\"1200\" height=\"538\" srcset=\"https:\/\/blog.razorpay.in\/wp-content\/uploads\/2019\/11\/Banner-Image-4.png 1200w, https:\/\/blog.razorpay.in\/wp-content\/uploads\/2019\/11\/Banner-Image-4-300x135.png 300w, https:\/\/blog.razorpay.in\/wp-content\/uploads\/2019\/11\/Banner-Image-4-1024x459.png 1024w, https:\/\/blog.razorpay.in\/wp-content\/uploads\/2019\/11\/Banner-Image-4-768x344.png 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/p>\n<p style=\"text-align: center;\"><a style=\"border-radius: 3px; background: #528FF0; padding: 15px; font-weight: 600; cursor: pointer; text-decoration: none; color: white;\" href=\"https:\/\/dashboard.razorpay.com\/signup?r=partner&amp;\">Join the Razorpay Partner Program!<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A good Developer Experience is when a developer can get their work done in an optimised manner, without being worried about multiple things.<\/p>\n","protected":false},"author":47,"featured_media":4113,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[69],"tags":[73,74,72],"class_list":{"0":"post-4112","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-razorpay-stories","8":"tag-developer-experience","9":"tag-react","10":"tag-tech"},"_links":{"self":[{"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/posts\/4112","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/users\/47"}],"replies":[{"embeddable":true,"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/comments?post=4112"}],"version-history":[{"count":1,"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/posts\/4112\/revisions"}],"predecessor-version":[{"id":21388,"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/posts\/4112\/revisions\/21388"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/media\/4113"}],"wp:attachment":[{"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/media?parent=4112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/categories?post=4112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/razorpay.com\/blog\/wp-json\/wp\/v2\/tags?post=4112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}