{"id":21653818,"url":"https://github.com/epranka/rates","last_synced_at":"2025-10-18T01:02:34.511Z","repository":{"id":39226042,"uuid":"240869077","full_name":"epranka/rates","owner":"epranka","description":"Currency exchanger in just about 100 lines using the Create React App (CRA)","archived":false,"fork":false,"pushed_at":"2023-01-05T07:25:05.000Z","size":4299,"stargazers_count":2,"open_issues_count":21,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-11T21:03:07.055Z","etag":null,"topics":["app","calculator","cra","create-react-app","currency","demo","exchanger","javascript","rates","tutorial","web-app"],"latest_commit_sha":null,"homepage":"https://rates.now.sh","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/epranka.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-02-16T10:06:07.000Z","updated_at":"2023-01-31T17:54:54.000Z","dependencies_parsed_at":"2023-02-03T18:16:57.784Z","dependency_job_id":null,"html_url":"https://github.com/epranka/rates","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epranka%2Frates","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epranka%2Frates/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epranka%2Frates/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epranka%2Frates/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/epranka","download_url":"https://codeload.github.com/epranka/rates/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248480437,"owners_count":21110937,"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":["app","calculator","cra","create-react-app","currency","demo","exchanger","javascript","rates","tutorial","web-app"],"created_at":"2024-11-25T08:20:51.635Z","updated_at":"2025-10-18T01:02:34.420Z","avatar_url":"https://github.com/epranka.png","language":"JavaScript","readme":"\u003ch1 align=\"center\" style=\"border-bottom: none;\"\u003e$ Currency Exchange App\u003c/h1\u003e\n\n\u003ch3 align=\"center\"\u003eUsing the Create React App (CRA)\u003c/h3\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://twitter.com/epranka\" title=\"follow on twitter\"\u003e\n    \u003cimg alt=\"twitter badge\" src=\"https://badgen.net/badge/icon/twitter/00acee?icon=twitter\u0026label\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://linkedin.com/in/epranka\" title=\"connect on linkedin\"\u003e\n      \u003cimg alt=\"linkedin badge\" src=\"https://badgen.net/badge/icon/linkedin/2867B2?label\u0026icon=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8%2BCjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgaWQ9InN2ZzQiCiAgIHZlcnNpb249IjEuMSIKICAgdmlld0JveD0iMCAwIDI0IDI0IgogICBoZWlnaHQ9IjI0IgogICB3aWR0aD0iMjQiPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTEwIj4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI%2BCiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ%2BCiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms%2BCiAgICA8L3JkZjpSREY%2BCiAgPC9tZXRhZGF0YT4KICA8ZGVmcwogICAgIGlkPSJkZWZzOCIgLz4KICA8cGF0aAogICAgIHN0eWxlPSJmaWxsOiNmZmZmZmYiCiAgICAgaWQ9InBhdGg4MjEiCiAgICAgZD0iTSA4LDE5IEggNSBWIDggaCAzIHoiIC8%2BCiAgPHBhdGgKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmIgogICAgIGlkPSJwYXRoODE5IgogICAgIGQ9Im0gNi41LDYuNzMyIGMgLTAuOTY2LDAgLTEuNzUsLTAuNzkgLTEuNzUsLTEuNzY0IDAsLTAuOTc0IDAuNzg0LC0xLjc2NCAxLjc1LC0xLjc2NCAwLjk2NiwwIDEuNzUsMC43OSAxLjc1LDEuNzY0IDAsMC45NzQgLTAuNzgzLDEuNzY0IC0xLjc1LDEuNzY0IHoiIC8%2BCiAgPHBhdGgKICAgICBzdHlsZT0iZmlsbDojZmZmZmZmIgogICAgIGlkPSJwYXRoMiIKICAgICBkPSJtIDIwLDE5IGggLTMgdiAtNS42MDQgYyAwLC0zLjM2OCAtNCwtMy4xMTMgLTQsMCBWIDE5IEggMTAgViA4IGggMyB2IDEuNzY1IGMgMS4zOTYsLTIuNTg2IDcsLTIuNzc3IDcsMi40NzYgeiIgLz4KPC9zdmc%2BCg%3D%3D\" /\u003e\u003c/a\u003e \n  \u003ca href=\"https://dev.to/epranka\" title=\"follow on dev.to\"\u003e\n    \u003cimg alt=\"dev.to badge\" src=\"https://badgen.net/badge/icon/dev.to/232221?label\u0026icon=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8%2BCjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgaWQ9InN2ZzQiCiAgIHZlcnNpb249IjEuMSIKICAgaGVpZ2h0PSIyMzIxIgogICB3aWR0aD0iMjUwMCIKICAgdmlld0JveD0iMCAzMiA0NDcuOTk5OTk5OTk5OTk5OTQgNDQ4Ij4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGExMCI%2BCiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ%2BaW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU%2BPC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE%2BCiAgPGRlZnMKICAgICBpZD0iZGVmczgiIC8%2BCiAgPHBhdGgKICAgICBkPSJNIDcxLjgxNTI4MiwxOTguNDUwNDUgSCA5MS4xNjI1MiBjIDQuMzA0NDAzLDAgOC42MjAxMjMsMS42MDg3MyAxMi45MjQ1MSw0LjgyNTkzIDQuMjkzMzEsMy4yMTcyMSA2LjQ1NjM5LDguMDUzODYgNi40Njc0OSwxNC40ODgyNyB2IDc3LjI2ODUxIGMgMCw2LjQ0NTUxIC0yLjE1MjIyLDExLjI3MTQ5IC02LjQ1NjYxLDE0LjQ4ODY5IC00LjMwNDM5NywzLjIxNzIyIC04LjYxOTY5Nyw0LjgyNTUyIC0xMi45MjQwOSw0LjgyNTUyIEggNzEuODE1MjgyIFogTSAzNi43Njk4MzksMTY0LjEwNDI4IFYgMzQ3LjY2MjE5IEggODguMjQ0OTYgYyA0MC43Njk3NCwwLjA2NjYgNTMuNjQ5OTIsLTMxLjU1MDc5IDUzLjY0OTkyLC01Mi40MTgyMyBsIC0wLjAxMTMsLTc4LjY4ODQxIGMgLTAuMDExMSwtMjAuODc4NTQgLTEzLjIzNDc5LC01Mi40NTEyNyAtNTIuNTUxMjMsLTUyLjQ1MTI3IHogbSAyMzYuNDE5ODYxLDAuMDQ0MyA0Mi43NDQ2MSwxNjAuNjM4MTUgYyAxMS43NDgzNCwyNy4zMjQwNCAzNy45ODUyMSwzNC4xMTM0NyA1Mi42MjkwMywwIEwgNDExLjIzMDE2LDE2NC4xNDg2MSBIIDM3NS4wODY0NSBMIDM0Mi4yODE4NywyOTAuMzA3MTkgMzA5LjMyMjU1LDE2NC4xNDg2MSBaIG0gLTg5LjY5MzIzLC0wLjAxMTMgYyAtMTIuMzY5NiwwLjMxMDYzIC0yMi4xNjUzNSwxMC41ODM2MiAtMjEuODY1ODIsMjIuOTUzMjEgdiAxMzguNzk0NSBjIDAuMzEwNjMsMTIuMzgwNjkgMTAuNjA1NTYsMjIuMTY1MzYgMjIuOTg2MjUsMjEuODQzNjUgaCA2OC45ODEzMyB2IC0zMi44MDQxNSBoIC01OS4xMTkwOSB2IC00Mi42MTE2MiBoIDM2LjEzMjg1IHYgLTMyLjgwNDE2IGggLTM2LjEzMjg1IHYgLTQyLjYyMjQ5IGggNTkuMTA3NzkgbCAwLjAxMTMsLTMyLjc0ODk1IHoiCiAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtzdHJva2Utd2lkdGg6MS4xMDkzODA2IgogICAgIGlkPSJwYXRoMiIgLz4KPC9zdmc%2BCg%3D%3D\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://medium.com/@epranka\" title=\"follow on medium\"\u003e\n      \u003cimg alt=\"medium badge\" src=\"https://badgen.net/badge/icon/medium/black?label\u0026icon=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8%2BCjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgaWQ9InN2ZzQiCiAgIHZlcnNpb249IjEuMSIKICAgY2xpcC1ydWxlPSJldmVub2RkIgogICBmaWxsLXJ1bGU9ImV2ZW5vZGQiCiAgIGhlaWdodD0iMjQiCiAgIHdpZHRoPSIyNCI%2BCiAgPG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhMTAiPgogICAgPHJkZjpSREY%2BCiAgICAgIDxjYzpXb3JrCiAgICAgICAgIHJkZjphYm91dD0iIj4KICAgICAgICA8ZGM6Zm9ybWF0PmltYWdlL3N2Zyt4bWw8L2RjOmZvcm1hdD4KICAgICAgICA8ZGM6dHlwZQogICAgICAgICAgIHJkZjpyZXNvdXJjZT0iaHR0cDovL3B1cmwub3JnL2RjL2RjbWl0eXBlL1N0aWxsSW1hZ2UiIC8%2BCiAgICAgICAgPGRjOnRpdGxlPjwvZGM6dGl0bGU%2BCiAgICAgIDwvY2M6V29yaz4KICAgIDwvcmRmOlJERj4KICA8L21ldGFkYXRhPgogIDxkZWZzCiAgICAgaWQ9ImRlZnM4IiAvPgogIDxwYXRoCiAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZiIKICAgICBpZD0icGF0aDIiCiAgICAgZD0iTTIuODQ2IDYuODg3Yy4wMy0uMjk1LS4wODMtLjU4Ni0uMzAzLS43ODRsLTIuMjQtMi43di0uNDAzaDYuOTU4bDUuMzc4IDExLjc5NSA0LjcyOC0xMS43OTVoNi42MzN2LjQwM2wtMS45MTYgMS44MzdjLS4xNjUuMTI2LS4yNDcuMzMzLS4yMTMuNTM4djEzLjQ5OGMtLjAzNC4yMDQuMDQ4LjQxMS4yMTMuNTM3bDEuODcxIDEuODM3di40MDNoLTkuNDEydi0uNDAzbDEuOTM5LTEuODgyYy4xOS0uMTkuMTktLjI0Ni4xOS0uNTM3di0xMC45MWwtNS4zODkgMTMuNjg4aC0uNzI4bC02LjI3NS0xMy42ODh2OS4xNzRjLS4wNTIuMzg1LjA3Ni43NzQuMzQ3IDEuMDUybDIuNTIxIDMuMDU4di40MDRoLTcuMTQ4di0uNDA0bDIuNTIxLTMuMDU4Yy4yNy0uMjc5LjM5LS42Ny4zMjUtMS4wNTJ2LTEwLjYwOHoiIC8%2BCjwvc3ZnPgo%3D\" /\u003e  \n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr /\u003e\n\nCurrency exchange app created with the Create React App (CRA) and [exchangeratesapi.io](https://exchangeratesapi.io/) API.\n\nTry it live on this [demo](https://rates.now.sh)\n\nThe main implementation is just about 100 lines.\n\n![Currency Exchange App](https://i.ibb.co/8KWQbgT/image.png)\n\n### The App Component\n\n```jsx\nimport React, { useState, useEffect } from \"react\";\nimport \"./App.css\";\nimport fetch from \"unfetch\";\nimport useSWR from \"swr\";\nimport {\n  Container,\n  Paper,\n  Grid,\n  TextField,\n  Select,\n  MenuItem\n} from \"@material-ui/core\";\n\nconst API_URL = \"https://api.exchangeratesapi.io\";\n\nconst fetcher = async path =\u003e {\n  const res = await fetch(API_URL + path);\n  const json = await res.json();\n  return json;\n};\n\nfunction App() {\n  const { data: currencies } = useSWR(\"/latest?base=EUR\", fetcher);\n\n  const [fromValue, setFromValue] = useState(1);\n  const [toValue, setToValue] = useState(1);\n\n  const [fromCurrency, setFromCurrency] = useState(\"EUR\");\n  const [toCurrency, setToCurrency] = useState(\"EUR\");\n\n  const handleFromCurrencyChange = e =\u003e {\n    setFromCurrency(e.target.value);\n  };\n\n  const handleToCurrencyChange = e =\u003e {\n    setToCurrency(e.target.value);\n  };\n\n  const handleFromValueChange = e =\u003e {\n    setFromValue(parseFloat(e.target.value));\n  };\n\n  const handleToValueChange = e =\u003e {\n    setToValue(parseFloat(e.target.value));\n  };\n\n  const convertFromTo = () =\u003e {\n    const fromRate =\n      fromCurrency === \"EUR\" ? 1 : currencies.rates[fromCurrency];\n    const valueInEur = fromValue / fromRate;\n    const toRate = toCurrency === \"EUR\" ? 1 : currencies.rates[toCurrency];\n    setToValue(valueInEur * toRate);\n  };\n\n  const convertToFrom = () =\u003e {\n    const toRate = toCurrency === \"EUR\" ? 1 : currencies.rates[toCurrency];\n    const valueInEur = toValue / toRate;\n    const fromRate =\n      fromCurrency === \"EUR\" ? 1 : currencies.rates[fromCurrency];\n    setFromValue(valueInEur * fromRate);\n  };\n\n  useEffect(() =\u003e {\n    convertFromTo();\n  }, [fromValue, toCurrency]);\n\n  useEffect(() =\u003e {\n    convertToFrom();\n  }, [toValue, fromCurrency]);\n\n  if (!currencies) {\n    return null;\n  }\n\n  return (\n    \u003cContainer className=\"currency-exchange-container\" fixed\u003e\n      \u003ch1\u003eCurrency exchange\u003c/h1\u003e\n      \u003cPaper\n        className=\"currency-exchange-paper\"\n        variant=\"outlined\"\n        elavation={1}\n      \u003e\n        \u003cGrid container spacing={3}\u003e\n          \u003cGrid item xs={6}\u003e\n            \u003cTextField\n              type=\"number\"\n              value={fromValue}\n              onChange={handleFromValueChange}\n            /\u003e\n          \u003c/Grid\u003e\n          \u003cGrid item xs={6}\u003e\n            \u003cTextField\n              type=\"number\"\n              value={toValue}\n              onChange={handleToValueChange}\n            /\u003e\n          \u003c/Grid\u003e\n          \u003cGrid item xs={6}\u003e\n            \u003cSelect value={fromCurrency} onChange={handleFromCurrencyChange}\u003e\n              \u003cMenuItem value={\"EUR\"}\u003eEUR\u003c/MenuItem\u003e\n              {Object.keys(currencies.rates).map((rate, key) =\u003e (\n                \u003cMenuItem key={key} value={rate}\u003e\n                  {rate}\n                \u003c/MenuItem\u003e\n              ))}\n            \u003c/Select\u003e\n          \u003c/Grid\u003e\n          \u003cGrid item xs={6}\u003e\n            \u003cSelect value={toCurrency} onChange={handleToCurrencyChange}\u003e\n              \u003cMenuItem value={\"EUR\"}\u003eEUR\u003c/MenuItem\u003e\n              {Object.keys(currencies.rates).map((rate, key) =\u003e (\n                \u003cMenuItem key={key} value={rate}\u003e\n                  {rate}\n                \u003c/MenuItem\u003e\n              ))}\n            \u003c/Select\u003e\n          \u003c/Grid\u003e\n        \u003c/Grid\u003e\n      \u003c/Paper\u003e\n    \u003c/Container\u003e\n  );\n}\n\nexport default App;\n```\n\n### CSS\n\n```css\nh1 {\n  font-weight: 300;\n  color: #636363;\n  margin-bottom: 3rem;\n}\n\n.currency-exchange-container {\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  justify-content: center;\n  height: 100vh;\n}\n\n.currency-exchange-paper {\n  max-width: 350px;\n  padding: 30px 30px 40px 30px;\n}\n\n.MuiInput-root {\n  width: 100%;\n}\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fepranka%2Frates","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fepranka%2Frates","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fepranka%2Frates/lists"}