{"id":15521374,"url":"https://github.com/andrewrosss/react-mui-dialog","last_synced_at":"2025-04-11T02:24:10.598Z","repository":{"id":53774839,"uuid":"346156462","full_name":"andrewrosss/react-mui-dialog","owner":"andrewrosss","description":"Simple declarative API for rendering Material UI Dialogs","archived":false,"fork":false,"pushed_at":"2021-03-26T05:49:04.000Z","size":3792,"stargazers_count":2,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-24T23:51:26.321Z","etag":null,"topics":["dialog","hooks","material-ui","material-ui-react","react","reactjs","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andrewrosss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-03-09T22:03:08.000Z","updated_at":"2023-12-06T14:29:01.000Z","dependencies_parsed_at":"2022-09-14T22:54:03.766Z","dependency_job_id":null,"html_url":"https://github.com/andrewrosss/react-mui-dialog","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewrosss%2Freact-mui-dialog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewrosss%2Freact-mui-dialog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewrosss%2Freact-mui-dialog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andrewrosss%2Freact-mui-dialog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andrewrosss","download_url":"https://codeload.github.com/andrewrosss/react-mui-dialog/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248328550,"owners_count":21085335,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dialog","hooks","material-ui","material-ui-react","react","reactjs","typescript"],"created_at":"2024-10-02T10:34:19.891Z","updated_at":"2025-04-11T02:24:10.581Z","avatar_url":"https://github.com/andrewrosss.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-mui-dialog\n\nSimple declarative API for rendering Material UI Dialogs written in TypeScript\n\n## Motivation\n\n`react-mui-dialog` was written while building a learning portal website. With around ~15 unique Dialogs, it became obvious that many of the dialogs shared much of the same structure and that we could configure and reuse the same components through a single extensible API.\n\n## Installation\n\n```bash\nnpm install react-mui-dialog --save\n```\n\n**Note:** react-mui-dialog (as the name suggests) assumes that you're using react, specifically version \u003e= 16.8, and material-ui.\n\n## Getting Started\n\nCheckout the [codesandbox](https://codesandbox.io/s/react-mui-dialog-demo-b1wsx?file=/src/Demo.tsx) for working examples.\n\nIn a nutshell there are 3 objects to know about.\n\n- `DialogProvider` - to be included near the root of the tree.\n- `useDialog` - the hook associated with the provider.\n- `openDialog` - The function which will configure, open, and handle the dialog.\n\nTo start, wrap your app (or some part of the subtree) in the provider, something like:\n\n```jsx\n// file: App.tsx\nimport * as React from \"react\";\n\nimport { DialogProvider } from \"react-mui-dialog\";\nimport { ThemeProvider } from \"@material-ui/core\";\n\n// ...\n\nexport const App = () =\u003e {\n  // ...\n\n  return (\n    \u003cThemeProvider\u003e\n      \u003cDialogProvider\u003e\n        {/*  ----------  other components  ----------  */}\n      \u003c/DialogProvider\u003e\n    \u003c/ThemeProvider\u003e\n  );\n};\n```\n\n**Note:** If you're using Material UI's ThemeProvider you probably want the DialogProvider to be a child of that component.\n\nThen in a nested component, hook into the dialog context. For example, let's create a button that will show the user a notification:\n\n```jsx\n// file: NotificationButton.tsx\nimport * as React from \"react\";\n\nimport { useDialog } from \"react-mui-dialog\";\n\nexport const NotificationButton = () =\u003e {\n  const { openDialog } = useDialog();\n\n  // ...\n};\n```\n\nNow inside of the nested component we can configure and open up the dialog in response to a user action (in this case after clicking a button) by using the `openDialog` function provided through the hook.\n\nIn the simplest case we show the user a dialog with a title, some text, and a button to dismiss the dialog (so, no cancel button, just a \"submit\" button letting us know that the user moved on past the dialog). An example might be delivering some kind of notification to a user, say when a user first logs into a site after some time and a dialog pops up to tell them about new features.\n\nContinuing the snippet above:\n\n```jsx\n// file: NotificationButton.tsx\nimport * as React from \"react\";\n\nimport { Button } from \"@material-ui/core\";\nimport { useDialog } from \"react-mui-dialog\";\n\nexport const NotificationButton = () =\u003e {\n  const { openDialog } = useDialog();\n\n  const handleClick = () =\u003e\n    openDialog({\n      // set the dialog's title\n      title: \"There's change in the Air!\",\n      // include some text to show the user, NOTE: this could be any arbitrary\n      // component, not just a string.\n      contentText: \"Here's what's new in version 2.0 ...\",\n      // don't render the cancel button, because in this case the only thing a\n      // user can do is \"dismiss\" the notification.\n      cancelButton: false,\n      // configure the submit button. MUI defaults to text buttons, let's\n      // use a contained button styled with the theme's primary color\n      submitButton: {\n        children: \"Dismiss\", // \u003c-- the button text\n        props: {\n          variant: \"contained\",\n          color: \"primary\",\n        },\n      },\n      // onSubmit is the action we take when the user \"accepts\" whatever\n      // the dialog is prompting them about. the function must return a\n      // promise, and since no fields have been defined (in this particular\n      // case), all we need to know is _that_ the user clicked the submit\n      // (dismiss) button.\n      onSubmit: async () =\u003e alert(\"The user dismissed this notification.\"),\n    });\n\n  return (\n    \u003cButton variant=\"contained\" color=\"primary\" onClick={handleClick}\u003e\n      Show Notification\n    \u003c/Button\u003e\n  );\n};\n```\n\nThis dialog config would yield something like:\n\n\u003cimg src='https://github.com/andrewrosss/react-mui-dialog/blob/master/assets/show-notifcation.png?raw=true' width='600'/\u003e\n\nAnd there we go, we can render this button somewhere in our app and can show the user a basic dialog.\n\n## Dialog Structure\n\nBefore getting into the examples it's worth touching on the general structure of a dialog that this package assumes. Basically, a dialog is viewed as just a fancy form which under the right conditions is configured and subsequently shown to a user as an overlay over the current page content.\n\nJust taking a quick looks at the components which \"make-up\" a [dialog](https://material-ui.com/components/dialogs/#api) as defined by the material-ui package we see a general structure:\n\n- Title\n- Content\n- Actions\n\nThis package imposes additional assumed structure on the contents of Dialog, the most important aspect being: **every dialog is a form**.\n\nWhat about a dialog with a title, some short text, and a button to close the dialog (something like the notification example above)? This can be viewed as a _\"trivial form\"_, basically just a submit button.\n\nThe question might then be, why not just use a button instead of a \"trivial form\", and the answer is that dialogs are purposefully interruptive requiring the user to take additional action. For example, a dialog which is presented to the user before deleting a document. The fact that the user simply submitted our \"trivial form\" is enough for us to know that we should carry on with the associated action.\n\nWith this in mind. This package assumes the following sturcture in addition to material-ui's Title, Content, Actions sections. Each Dialog (form), has:\n\n- Title\n- Content, where the content can have:\n  - Text describing the reason for the interruption in flow.\n  - Any number of optional fields (possibly 0 - by default text fields) to collect additional information from the user, if required.\n- Actions, which by default are:\n  - A cancel button which always closes the dialog and \"aborts\" submitting the form.\n  - A submit button which the user clicks to submit the form, effectively accepting or continuing with the associated action.\n\n## Examples\n\nThe section outlines some (most) of the available configuration that can be passed to calls to `openDialog`.\n\nA working codesandbox is available [here](https://codesandbox.io/s/react-mui-dialog-demo-b1wsx?file=/src/Demo.tsx). The code in the sandbox can also be viewed on github [here](https://github.com/andrewrosss/react-mui-dialog/blob/master/example/index.tsx).\n\n### Dialog with custom buttons\n\nWe can customize the cancel and submit buttons which are shown to the user:\n\n```tsx\n// elsewhere ...\nimport { Typography } from \"@material-ui/core\";\n\n// ...\n\nconst { openDialog, closeDialog } = useDialog();\n//                  ^^^^^^^^^^^\n\n// ...\n\nopenDialog({\n  title: \"Delete this document?\",\n  // a component this time\n  contentText: (\n    \u003cTypography color=\"textSecondary\"\u003e\n      You are about to delete the document \u003cb\u003e{docName}\u003c/b\u003e. This cannot be\n      undone.\n    \u003c/Typography\u003e\n  ),\n  // In this case we'll pass our own button components.\n  // NOTE: Because we're passing our own component we have to\n  //       handle closing the dialog when we click cancel\n  cancelButton: {\n    component: \u003cCustomButton onClick={closeDialog}\u003eCancel\u003c/CustomButton\u003e,\n  },\n  // NOTE: make sure to set type='submit' for the submit button\n  submitButton: {\n    component: (\n      \u003cHighEmphasisCustomButton type=\"submit\" variant=\"contained\"\u003e\n        Yes I'm sure, delete this document\n      \u003c/HighEmphasisCustomButton\u003e\n    ),\n  },\n  onSubmit: async () =\u003e\n    alert(`Deleting document name [${docName}] with ID [${docId}]`),\n});\n```\n\n\u003cimg src='https://github.com/andrewrosss/react-mui-dialog/blob/master/assets/delete.png?raw=true' width='600'/\u003e\n\n### Dialog with fields\n\nBy default you can easily add-in (text) fields. If you require other types of fields consult the example after this one. Importantly, this package uses `formik` under the hood, if you're familiar with formik then some of the nomenclature should sound familiar.\n\n```tsx\n// elsewhere ...\nimport * as Yup from \"yup\";\n\n// ...\n\nconst { openDialog } = useDialog();\n\n// ...\n\nopenDialog({\n  title: \"Subscribe\",\n  contentText:\n    \"To subscribe to this website, please enter your email address here. We will send updates occasionally.\",\n  // Render formik fields in the dialog by specifying fields (below), each\n  // key is used as the name of a field in the formik form. There is\n  // a 1:1 mapping between the keys below and fields in the form.\n  fields: {\n    emailAddress: {\n      // behind the scenes this packages gathers all the initialValue\n      // values found in this \"fields\" object, constructs an\n      // 'initialValues' object and passes that to the \u003cFormik /\u003e component\n      initialValue: \"\",\n      // for convenience we could omit 'label' and react-mui-dialog would use this\n      // field's name for the label\n      label: \"Email Address\",\n      // These props are passed directly to the underlying\n      // formik \u003cField /\u003e component.\n      fieldProps: { variant: \"filled\" },\n    },\n  },\n  // optional validationSchema, defined just as you would with\n  // formik, used to validate the fields.\n  validationSchema: Yup.object({\n    emailAddress: Yup.string()\n      .required(\"This field is required\")\n      .email(\"Must be a valid email\"),\n  }),\n  cancelButton: { children: \"No Thanks\" },\n  submitButton: { children: \"Subscribe\" },\n  // the keys of the fields object (above) are how you reference\n  // values received by the form (as is typical with formik)\n  onSubmit: async ({ emailAddress }) =\u003e\n    alert(`Added email [${emailAddress}] to the mailing list!`),\n});\n```\n\nThis config would result in something like:\n\n\u003cimg src='https://github.com/andrewrosss/react-mui-dialog/blob/master/assets/subscribe.png?raw=true' width='600'/\u003e\n\n### Dialog with Custom Fields\n\nFor more control over the fields which are rendered, or if we simply want something other than text fields, we can pass formik `\u003cField /\u003e` components directory to `openDialog`.\n\n**Note:** this is a heavily truncated example, check out the [codesandbox](https://codesandbox.io/s/react-mui-dialog-demo-b1wsx?file=/src/Demo.tsx) for a working example.\n\n```tsx\n// elsewhere ...\nimport * as Yup from \"yup\";\nimport { CheckboxWithLabel, Select } from \"formik-material-ui\";\nimport { FormControl, InputLabel, MenuItem } from \"@material-ui/core\";\nimport { Field } from \"formik\";\n\n// ...\n\nconst user = getUserProfile();\n\n// ...\n\nopenDialog({\n  title: \"Profile Settings\",\n  contentText: null,\n  fields: {\n    username: {\n      initialValue: user.username,\n      // NOTE: we omit passing a label\n    },\n    // here we render something other than a text field by modifying\n    // the props that are passed to the formik \u003cField /\u003e component.\n    onMailingList: {\n      initialValue: user.onMailingList,\n      fieldProps: {\n        component: CheckboxWithLabel,\n        type: \"checkbox\",\n        Label: { label: \"Receive newsletter\" },\n      },\n    },\n    // Here we pass our own component, if [fieldName].component is\n    // specified then this component will be rendered and\n    // [fieldName].fieldProps will be ignored.\n    notificationRetention: {\n      initialValue: user.notificationRetention,\n      component: (\n        \u003cFormControl\u003e\n          \u003cInputLabel htmlFor=\"notificationRetention\"\u003e\n            Keep notifications for\n          \u003c/InputLabel\u003e\n          \u003cField\n            component={Select}\n            name=\"notificationRetention\"\n            inputProps={{\n              id: \"notificationRetention\",\n            }}\n          \u003e\n            \u003cMenuItem value={\"1_week\"}\u003e1 Week\u003c/MenuItem\u003e\n            \u003cMenuItem value={\"2_weeks\"}\u003e2 Weeks\u003c/MenuItem\u003e\n            \u003cMenuItem value={\"1_month\"}\u003e1 Month\u003c/MenuItem\u003e\n          \u003c/Field\u003e\n        \u003c/FormControl\u003e\n      ),\n    },\n  },\n  validationSchema: Yup.object({\n    username: Yup.string().required(\"username cannot be empty\"),\n    onMailingList: Yup.boolean(),\n    notificationRetention: Yup.string(),\n  }),\n  cancelButton: { children: \"Close\" },\n  submitButton: {\n    children: \"Save\",\n    props: { variant: \"contained\", color: \"secondary\" },\n  },\n  onSubmit: async ({ username, onMailingList, notificationRetention }) =\u003e\n    alert(\n      `Saving settings Username [${username}], Receive newsletter [${onMailingList}], Keep notifications for [${notificationRetention}]`\n    ),\n});\n```\n\nThis config would yeild something like:\n\n\u003cimg src='https://github.com/andrewrosss/react-mui-dialog/blob/master/assets/settings.png?raw=true' width='600'/\u003e\n\n### Custom Everything\n\nFinally if you just want something completely custom you can override the entire contents of the dialog for something that suits your needs:\n\nLet's make ourselves a custom form. Note this component has nothing to do with `react-mui-dialog`:\n\n```tsx\nconst CustomForm: React.FC\u003c{ onCancel: () =\u003e void) }\u003e = ({ onCancel }) =\u003e {\n  const [state, setState] = React.useState({\n    email: \"email@domain.com\",\n    terms: false,\n    mailing: true,\n  });\n  const handleTextChange = (e: React.ChangeEvent\u003cHTMLInputElement\u003e) =\u003e {\n    setState({ ...state, [e.target.name]: e.target.value });\n  };\n  const handleCheckChange = (e: React.ChangeEvent\u003cHTMLInputElement\u003e) =\u003e {\n    setState({ ...state, [e.target.name]: e.target.checked });\n  };\n\n  const handleSubmit = (e: React.FormEvent) =\u003e {\n    e.preventDefault();\n    alert(\n      `Email [${state.email}] with answers to terms [${state.terms}] and mailing [${state.mailing}]`\n    );\n    onCancel();\n  };\n\n  return (\n    \u003cform\n      onSubmit={handleSubmit}\n      style={{\n        display: \"flex\",\n        flexDirection: \"column\",\n        alignItems: \"start\",\n        gap: \"16px\",\n        padding: \"16px\",\n      }}\n    \u003e\n      \u003cTypography variant=\"h6\"\u003eTerms \u0026 Privacy\u003c/Typography\u003e\n      \u003cTypography color=\"textSecondary\"\u003eWe've updated our terms ...\u003c/Typography\u003e\n      \u003cTextField\n        type=\"email\"\n        name=\"email\"\n        label={\"Updated Email\"}\n        variant=\"filled\"\n        fullWidth\n        required\n        value={state.email}\n        onChange={handleTextChange}\n      /\u003e\n      \u003cdiv style={{ display: \"flex\", alignItems: \"center\" }}\u003e\n        \u003cFormControlLabel\n          control={\n            \u003cCheckbox\n              name=\"terms\"\n              checked={state.terms}\n              required\n              onChange={handleCheckChange}\n            /\u003e\n          }\n          label=\"Accept Terms\"\n        /\u003e\n        \u003cFormControlLabel\n          control={\n            \u003cCheckbox\n              name=\"mailing\"\n              checked={state.mailing}\n              onChange={handleCheckChange}\n            /\u003e\n          }\n          label=\"Receive newsletter\"\n        /\u003e\n      \u003c/div\u003e\n      \u003cdiv style={{ alignSelf: \"end\", display: \"flex\", gap: \"16px\" }}\u003e\n        \u003cButton onClick={onCancel}\u003eCancel\u003c/Button\u003e\n        \u003cButton variant=\"contained\" type=\"submit\"\u003e\n          Udpate\n        \u003c/Button\u003e\n      \u003c/div\u003e\n    \u003c/form\u003e\n  );\n};\n```\n\nAnd then let's pass this component to `react-mui-dialog` to handle displaying it to the user:\n\n```tsx\n// elsewhere ...\nconst { openDialog, closeDialog } = useDialog();\n\n// ...\n\nopenDialog({\n  customContent: \u003cCustomForm onCancel={closeDialog} /\u003e,\n});\n```\n\nWhich would yield something like:\n\n\u003cimg src='https://github.com/andrewrosss/react-mui-dialog/blob/master/assets/custom.png?raw=true' width='600'/\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewrosss%2Freact-mui-dialog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandrewrosss%2Freact-mui-dialog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandrewrosss%2Freact-mui-dialog/lists"}