├── 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 | 
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 |
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 |
--------------------------------------------------------------------------------