├── src ├── App.css ├── index.css ├── main.jsx ├── App.jsx └── components │ ├── dropdown.jsx │ └── currency-convertor.jsx ├── postcss.config.js ├── vite.config.js ├── tailwind.config.js ├── README.md ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── package.json └── public └── vite.svg /src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | } 4 | 5 | @tailwind base; 6 | @tailwind components; 7 | @tailwind utilities; 8 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Build a Currency Converter with React JS and Tailwind CSS 2 | ## [Click Here to Watch Full tutorial on Youtube](https://www.youtube.com/watch?v=Y1Q4XXXmVk4) 3 | 4 | ![currency convertor](https://github.com/piyush-eon/currency-converter/assets/51760520/17077560-f167-4291-aeb9-069f281f3406) 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import CurrencyConvertor from "./components/currency-convertor"; 3 | 4 | function App() { 5 | return ( 6 |
7 |
8 | 9 |
10 |
11 | ); 12 | } 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "currency-convertor", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-icons": "^5.0.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.66", 19 | "@types/react-dom": "^18.2.22", 20 | "@vitejs/plugin-react": "^4.2.1", 21 | "autoprefixer": "^10.4.19", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.1", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.6", 26 | "postcss": "^8.4.38", 27 | "tailwindcss": "^3.4.3", 28 | "vite": "^5.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/dropdown.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import {HiOutlineStar, HiStar} from "react-icons/hi2"; 3 | 4 | const CurrencyDropdown = ({ 5 | currencies, 6 | currency, 7 | setCurrency, 8 | favorites, 9 | handleFavorite, 10 | title = "", 11 | }) => { 12 | const isFavorite = (curr) => favorites.includes(curr); 13 | 14 | return ( 15 |
16 | 22 | 23 |
24 | 47 | 48 | 54 |
55 |
56 | ); 57 | }; 58 | 59 | export default CurrencyDropdown; 60 | -------------------------------------------------------------------------------- /src/components/currency-convertor.jsx: -------------------------------------------------------------------------------- 1 | import {useEffect} from "react"; 2 | import {useState} from "react"; 3 | import CurrencyDropdown from "./dropdown"; 4 | import {HiArrowsRightLeft} from "react-icons/hi2"; 5 | 6 | const CurrencyConverter = () => { 7 | const [currencies, setCurrencies] = useState([]); 8 | const [amount, setAmount] = useState(1); 9 | const [fromCurrency, setFromCurrency] = useState("USD"); 10 | const [toCurrency, setToCurrency] = useState("INR"); 11 | const [convertedAmount, setConvertedAmount] = useState(null); 12 | const [converting, setConverting] = useState(false); 13 | const [favorites, setFavorites] = useState( 14 | JSON.parse(localStorage.getItem("favorites")) || ["INR", "EUR"] 15 | ); 16 | 17 | // Currencies -> https://api.frankfurter.app/currencies 18 | const fetchCurrencies = async () => { 19 | try { 20 | const res = await fetch("https://api.frankfurter.app/currencies"); 21 | const data = await res.json(); 22 | 23 | setCurrencies(Object.keys(data)); 24 | } catch (error) { 25 | console.error("Error Fetching", error); 26 | } 27 | }; 28 | 29 | useEffect(() => { 30 | fetchCurrencies(); 31 | }, []); 32 | 33 | console.log(currencies); 34 | 35 | // Conversion -> https://api.frankfurter.app/latest?amount=1&from=USD&to=INR 36 | const convertCurrency = async () => { 37 | if (!amount) return; 38 | setConverting(true); 39 | try { 40 | const res = await fetch( 41 | `https://api.frankfurter.app/latest?amount=${amount}&from=${fromCurrency}&to=${toCurrency}` 42 | ); 43 | const data = await res.json(); 44 | 45 | setConvertedAmount(data.rates[toCurrency] + " " + toCurrency); 46 | } catch (error) { 47 | console.error("Error Fetching", error); 48 | } finally { 49 | setConverting(false); 50 | } 51 | }; 52 | 53 | const handleFavorite = (currency) => { 54 | let updatedFavorites = [...favorites]; 55 | 56 | if (favorites.includes(currency)) { 57 | updatedFavorites = updatedFavorites.filter((fav) => fav !== currency); 58 | } else { 59 | updatedFavorites.push(currency); 60 | } 61 | 62 | setFavorites(updatedFavorites); 63 | localStorage.setItem("favorites", JSON.stringify(updatedFavorites)); 64 | }; 65 | 66 | const swapCurrencies = () => { 67 | setFromCurrency(toCurrency); 68 | setToCurrency(fromCurrency); 69 | }; 70 | 71 | return ( 72 |
73 |

74 | Currency Converter 75 |

76 | 77 |
78 | 86 | {/* swap currency button */} 87 |
88 | 94 |
95 | 103 |
104 | 105 |
106 | 112 | setAmount(e.target.value)} 115 | type="number" 116 | className="w-full p-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 mt-1" 117 | /> 118 |
119 | 120 |
121 | 128 |
129 | 130 | {convertedAmount && ( 131 |
132 | Converted Amount: {convertedAmount} 133 |
134 | )} 135 |
136 | ); 137 | }; 138 | 139 | export default CurrencyConverter; 140 | --------------------------------------------------------------------------------