├── .gitignore ├── README.md ├── components ├── index.js └── render │ ├── Button.js │ ├── Div.js │ ├── Li.js │ ├── P.js │ ├── VerticalDots.js │ ├── index.js │ └── renderComponent.js ├── config.js ├── package-lock.json ├── package.json ├── pages ├── _app.js └── index.js ├── postcss.config.js ├── public ├── favicon.ico └── vercel.svg └── tailwind.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Create React Components from JSON 2 | 3 | This repo is from a complete step-by-step tutorial by [Skillthrive](https://youtu.be/NMxMWOZC-Ec). 4 | -------------------------------------------------------------------------------- /components/index.js: -------------------------------------------------------------------------------- 1 | export * from './render'; 2 | -------------------------------------------------------------------------------- /components/render/Button.js: -------------------------------------------------------------------------------- 1 | export const Button = ({ className, style, id, children }) => { 2 | return ( 3 | 6 | ); 7 | }; 8 | -------------------------------------------------------------------------------- /components/render/Div.js: -------------------------------------------------------------------------------- 1 | export const Div = ({ className, style, id, children }) => { 2 | return ( 3 |
4 | {children} 5 |
6 | ); 7 | }; 8 | -------------------------------------------------------------------------------- /components/render/Li.js: -------------------------------------------------------------------------------- 1 | export const Li = ({ className, style, id, children }) => { 2 | return ( 3 |
  • 4 | {children} 5 |
  • 6 | ); 7 | }; 8 | -------------------------------------------------------------------------------- /components/render/P.js: -------------------------------------------------------------------------------- 1 | export const P = ({ className, style, id, children }) => { 2 | return ( 3 |

    4 | {children} 5 |

    6 | ); 7 | }; 8 | -------------------------------------------------------------------------------- /components/render/VerticalDots.js: -------------------------------------------------------------------------------- 1 | import { BiDotsVerticalRounded } from 'react-icons/bi'; 2 | 3 | export const VerticalDots = ({ className, style, id }) => { 4 | return ; 5 | }; 6 | -------------------------------------------------------------------------------- /components/render/index.js: -------------------------------------------------------------------------------- 1 | export * from './renderComponent'; 2 | -------------------------------------------------------------------------------- /components/render/renderComponent.js: -------------------------------------------------------------------------------- 1 | import { createElement } from 'react'; 2 | import { Div } from './Div'; 3 | import { Li } from './Li'; 4 | import { P } from './P'; 5 | import { Button } from './Button'; 6 | import { VerticalDots } from './VerticalDots'; 7 | 8 | const keysToComponentMap = { 9 | div: Div, 10 | li: Li, 11 | p: P, 12 | button: Button, 13 | verticalDots: VerticalDots, 14 | }; 15 | 16 | const stylesMap = (styles) => { 17 | let mappedStyles = {}; 18 | styles.forEach((style) => { 19 | mappedStyles[style.name] = style.value; 20 | }); 21 | return mappedStyles; 22 | }; 23 | 24 | export const renderComponent = (config) => { 25 | if (typeof keysToComponentMap[config.component] !== 'undefined') { 26 | return createElement( 27 | keysToComponentMap[config.component], 28 | { 29 | id: config.id, 30 | key: config.id, 31 | className: config.className ? config.className : null, 32 | ariaHidden: config.ariaHidden ? config.ariaHidden : null, 33 | style: config.styles ? stylesMap(config.styles) : null, 34 | }, 35 | config.children && 36 | (typeof config.children === 'string' 37 | ? config.children 38 | : config.children.map((c) => renderComponent(c))) 39 | ); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | export const config = { 2 | component: 'li', 3 | id: 'cardWrapper', 4 | className: 'col-span-1 flex shadow-sm rounded-md', 5 | children: [ 6 | { 7 | component: 'div', 8 | id: 'initialWrapper', 9 | className: 10 | 'flex-shrink-0 flex items-center justify-center w-16 text-white text-sm font-medium rounded-l-md', 11 | styles: [ 12 | { 13 | name: 'backgroundColor', 14 | value: '#6366F1', 15 | }, 16 | ], 17 | children: 'HB', 18 | }, 19 | { 20 | component: 'div', 21 | id: 'infoWrapper', 22 | className: 23 | 'flex-1 flex items-center justify-between border-t border-r border-b border-gray-200 bg-white rounded-r-md truncate', 24 | children: [ 25 | { 26 | component: 'div', 27 | id: 'info', 28 | className: 'flex-1 px-4 py-2 text-sm truncate', 29 | children: [ 30 | { 31 | component: 'p', 32 | id: 'title', 33 | className: 'text-gray-900 font-medium hover:text-gray-600', 34 | children: 'GraphQL', 35 | }, 36 | { 37 | component: 'p', 38 | id: 'readTime', 39 | className: 'text-gray-500', 40 | children: '10 min read', 41 | }, 42 | ], 43 | }, 44 | { 45 | component: 'div', 46 | id: 'buttonWrapper', 47 | className: 'flex-shrink-0 pr-2', 48 | children: [ 49 | { 50 | component: 'button', 51 | id: 'optionButton', 52 | className: 53 | 'w-8 h-8 bg-white inline-flex items-center justify-center text-gray-400 rounded-full bg-transparent hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500', 54 | children: [ 55 | { 56 | component: 'span', 57 | id: 'optionButtonSr', 58 | className: 'sr-only', 59 | children: 'Open Options', 60 | }, 61 | { 62 | component: 'verticalDots', 63 | id: 'verticalDots', 64 | className: 'w-5 h-5', 65 | ariaHidden: 'true', 66 | }, 67 | ], 68 | }, 69 | ], 70 | }, 71 | ], 72 | }, 73 | ], 74 | }; 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "with-tailwindcss", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start" 9 | }, 10 | "dependencies": { 11 | "next": "latest", 12 | "react": "^17.0.2", 13 | "react-dom": "^17.0.2", 14 | "react-icons": "^4.2.0" 15 | }, 16 | "devDependencies": { 17 | "autoprefixer": "^10.2.6", 18 | "postcss": "^8.3.5", 19 | "tailwindcss": "^2.2.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pages/_app.js: -------------------------------------------------------------------------------- 1 | import 'tailwindcss/tailwind.css' 2 | 3 | function MyApp({ Component, pageProps }) { 4 | return 5 | } 6 | 7 | export default MyApp 8 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | import { BiDotsVerticalRounded } from 'react-icons/bi'; 2 | 3 | import { config } from '../config'; 4 | import { renderComponent } from '../components'; 5 | 6 | export default function Home() { 7 | const project = { 8 | name: 'Graph API', 9 | initials: 'GA', 10 | href: '#', 11 | members: 16, 12 | bgColor: 'bg-pink-600', 13 | }; 14 | 15 | const classNames = (...classes) => { 16 | return classes.filter(Boolean).join(' '); 17 | }; 18 | 19 | return ( 20 |
    21 |
      22 |
    • 23 |
      29 | {project.initials} 30 |
      31 |
      32 |
      33 |

      34 | {project.name} 35 |

      36 |

      {project.members} Members

      37 |
      38 |
      39 | 43 |
      44 |
      45 |
    • 46 | {renderComponent(config)} 47 |
    48 |
    49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | // If you want to use other PostCSS plugins, see the following: 2 | // https://tailwindcss.com/docs/using-with-preprocessors 3 | module.exports = { 4 | plugins: { 5 | tailwindcss: {}, 6 | autoprefixer: {}, 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hunterbecton/react-from-json/27f218dd4805e1e80166c0f902de052ee4d13285/public/favicon.ico -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: 'jit', 3 | purge: { 4 | content: [ 5 | './pages/**/*.{js,ts,jsx,tsx}', 6 | './components/**/*.{js,ts,jsx,tsx}', 7 | ], 8 | safelist: [ 9 | 'col-span-1', 10 | 'flex', 11 | 'shadow-sm', 12 | 'rounded-md', 13 | 'flex-shrink-0', 14 | 'items-center', 15 | 'justify-center', 16 | 'w-16', 17 | 'text-white', 18 | 'text-sm', 19 | 'font-medium', 20 | 'rounded-l-md', 21 | 'flex-1', 22 | 'px-4', 23 | 'py-2', 24 | 'pr-2', 25 | 'w-8', 26 | 'h-8', 27 | 'inline-flex', 28 | 'text-gray-400', 29 | 'rounded-full', 30 | 'bg-transparent', 31 | 'hover:text-gray-500', 32 | 'focus:outline-none', 33 | 'focus:ring-2', 34 | 'focus:ring-offset-2', 35 | 'focus:ring-indigo-500', 36 | ], 37 | }, 38 | darkMode: false, // or 'media' or 'class' 39 | theme: { 40 | extend: {}, 41 | }, 42 | variants: { 43 | extend: {}, 44 | }, 45 | plugins: [], 46 | }; 47 | --------------------------------------------------------------------------------