├── .npmrc
├── epicshop
├── .npmrc
├── in-browser-tests.spec.js
├── update-deps.sh
├── package.json
├── Dockerfile
├── setup-custom.js
├── playwright.config.js
├── fly.yaml
└── setup.js
├── public
├── favicon.ico
├── og
│ ├── background.png
│ └── logo.svg
├── react.js
├── images
│ └── instructor.png
├── react-dom
│ └── client.js
├── favicon.svg
└── logo.svg
├── .prettierignore
├── exercises
├── FINISHED.mdx
├── 03.using-jsx
│ ├── 04.solution.nesting
│ │ ├── README.mdx
│ │ ├── index.html
│ │ └── content.test.ts
│ ├── 05.solution.fragments
│ │ ├── README.mdx
│ │ ├── index.html
│ │ ├── index.test.ts
│ │ └── content.test.ts
│ ├── FINISHED.mdx
│ ├── 02.solution.interpolation
│ │ ├── README.mdx
│ │ ├── content.test.ts
│ │ ├── index.html
│ │ └── index.test.ts
│ ├── 03.solution.spread
│ │ ├── README.mdx
│ │ ├── index.test.ts
│ │ ├── content.test.ts
│ │ └── index.html
│ ├── 04.problem.nesting
│ │ ├── README.mdx
│ │ └── index.html
│ ├── 01.solution.compiler
│ │ ├── index.html
│ │ ├── content.test.ts
│ │ ├── index.test.ts
│ │ └── README.mdx
│ ├── 01.problem.compiler
│ │ ├── README.mdx
│ │ └── index.html
│ ├── 02.problem.interpolation
│ │ ├── index.html
│ │ └── README.mdx
│ ├── 03.problem.spread
│ │ ├── index.html
│ │ └── README.mdx
│ ├── 05.problem.fragments
│ │ ├── index.html
│ │ └── README.mdx
│ └── README.mdx
├── 02.raw-react
│ ├── 03.solution.deep-nesting
│ │ ├── README.mdx
│ │ ├── index.html
│ │ └── index.test.ts
│ ├── FINISHED.mdx
│ ├── 02.solution.nesting
│ │ ├── README.mdx
│ │ ├── index.html
│ │ └── index.test.ts
│ ├── 01.solution.elements
│ │ ├── README.mdx
│ │ ├── index.html
│ │ └── index.test.ts
│ ├── 03.problem.deep-nesting
│ │ ├── README.mdx
│ │ └── index.html
│ ├── 02.problem.nesting
│ │ ├── README.mdx
│ │ └── index.html
│ ├── 01.problem.elements
│ │ ├── index.html
│ │ └── README.mdx
│ └── README.mdx
├── 04.components
│ ├── 04.solution.props
│ │ ├── README.mdx
│ │ ├── content.test.ts
│ │ └── index.html
│ ├── FINISHED.mdx
│ ├── 02.solution.raw
│ │ ├── README.mdx
│ │ ├── index.test.ts
│ │ ├── content.test.ts
│ │ └── index.html
│ ├── 01.solution.function
│ │ ├── README.mdx
│ │ ├── index.test.ts
│ │ ├── index.html
│ │ └── content.test.ts
│ ├── 03.solution.jsx
│ │ ├── index.test.ts
│ │ ├── index.html
│ │ ├── content.test.ts
│ │ └── README.mdx
│ ├── 04.problem.props
│ │ ├── README.mdx
│ │ └── index.html
│ ├── 01.problem.function
│ │ ├── index.html
│ │ └── README.mdx
│ ├── 03.problem.jsx
│ │ ├── index.html
│ │ └── README.mdx
│ ├── 02.problem.raw
│ │ ├── index.html
│ │ └── README.mdx
│ └── README.mdx
├── 01.js-hello-world
│ ├── FINISHED.mdx
│ ├── 01.solution.hello
│ │ ├── README.mdx
│ │ ├── index.html
│ │ └── index.test.ts
│ ├── 02.problem.root
│ │ ├── README.mdx
│ │ └── index.html
│ ├── 02.solution.root
│ │ ├── README.mdx
│ │ ├── index.html
│ │ └── index.test.ts
│ ├── 01.problem.hello
│ │ ├── README.mdx
│ │ └── index.html
│ └── README.mdx
└── README.mdx
├── LICENSE.md
├── tsconfig.json
├── .gitignore
├── eslint.config.js
├── .github
└── workflows
│ └── validate.yml
├── package.json
└── README.md
/.npmrc:
--------------------------------------------------------------------------------
1 | legacy-peer-deps=true
2 | registry=https://registry.npmjs.org/
3 |
--------------------------------------------------------------------------------
/epicshop/.npmrc:
--------------------------------------------------------------------------------
1 | legacy-peer-deps=true
2 | registry=https://registry.npmjs.org/
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/get-started-with-react/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/og/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/get-started-with-react/HEAD/public/og/background.png
--------------------------------------------------------------------------------
/public/react.js:
--------------------------------------------------------------------------------
1 | export * from 'https://esm.sh/react?dev'
2 | export { default } from 'https://esm.sh/react?dev'
3 |
--------------------------------------------------------------------------------
/public/images/instructor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/epicweb-dev/get-started-with-react/HEAD/public/images/instructor.png
--------------------------------------------------------------------------------
/public/react-dom/client.js:
--------------------------------------------------------------------------------
1 | export * from 'https://esm.sh/react-dom/client?dev'
2 | export { default } from 'https://esm.sh/react-dom/client?dev'
3 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | **/build/**
3 | **/public/build/**
4 | **/public/babel-standalone.js
5 | .env
6 | **/package-lock.json
7 | **/playwright-report/**
--------------------------------------------------------------------------------
/exercises/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Get Started with React 🏃➡️
2 |
3 |
4 |
5 | Hooray! You're all done! 👏👏
6 |
--------------------------------------------------------------------------------
/exercises/03.using-jsx/04.solution.nesting/README.mdx:
--------------------------------------------------------------------------------
1 | # Nesting JSX
2 |
3 |
4 |
5 | 👨💼 Great work! Isn't that so much better than raw `createElement` calls? 😆
6 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | This material is available for private, non-commercial use under the
2 | [GPL version 3](http://www.gnu.org/licenses/gpl-3.0-standalone.html). If you
3 | would like to use this material to conduct your own workshop, please contact us
4 | at team@epicweb.dev
5 |
--------------------------------------------------------------------------------
/exercises/02.raw-react/03.solution.deep-nesting/README.mdx:
--------------------------------------------------------------------------------
1 | # Deep Nesting Elements
2 |
3 |
4 |
5 | 👨💼 Great job! We can go as deep as we want with this stuff!
6 |
--------------------------------------------------------------------------------
/exercises/02.raw-react/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Raw React APIs
2 |
3 |
4 |
5 | 👨💼 Wahoo! I'll bet you're excited to get into JSX after working with the raw
6 | APIs huh? 😅
7 |
--------------------------------------------------------------------------------
/exercises/03.using-jsx/05.solution.fragments/README.mdx:
--------------------------------------------------------------------------------
1 | # Fragments
2 |
3 |
4 |
5 | 👨💼 Great! Now you can put elements side-by-side without worrying about their
6 | parent element.
7 |
--------------------------------------------------------------------------------
/exercises/04.components/04.solution.props/README.mdx:
--------------------------------------------------------------------------------
1 | # Props
2 |
3 |
4 |
5 | 👨💼 Great work! Now you have a solid understanding of how React treats JSX and
6 | custom components! Well done.
7 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["**/*.ts", "**/*.tsx"],
3 | "extends": ["@epic-web/config/typescript"],
4 | "compilerOptions": {
5 | // keep things easy for the exercises
6 | "noUncheckedIndexedAccess": false,
7 | "paths": {
8 | "#*": ["./*"]
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | workspace/
4 | **/.cache/
5 | **/build/
6 | **/public/build
7 | /playground
8 | **/tsconfig.tsbuildinfo
9 |
10 | # in a real app you'd want to not commit the .env
11 | # file as well, but since this is for a workshop
12 | # we're going to keep them around.
13 | # .env
14 |
--------------------------------------------------------------------------------
/exercises/01.js-hello-world/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Hello World in JS
2 |
3 |
4 |
5 | 👨💼 Great job! You did it! Now's your opportunity to review what you learned.
6 | Writing down what you've learned helps you to remember it better.
7 |
--------------------------------------------------------------------------------
/exercises/02.raw-react/02.solution.nesting/README.mdx:
--------------------------------------------------------------------------------
1 | # Nesting Elements
2 |
3 |
4 |
5 | 👨💼 Great! Now we've got a nice way to nest our React elements so we can build
6 | complex structures in our generated HTML. Let's go **even deeper**!
7 |
--------------------------------------------------------------------------------
/epicshop/in-browser-tests.spec.js:
--------------------------------------------------------------------------------
1 | import { dirname, resolve } from 'path'
2 | import { fileURLToPath } from 'url'
3 | import { setupInBrowserTests } from '@epic-web/workshop-utils/playwright.server'
4 |
5 | const __dirname = dirname(fileURLToPath(import.meta.url))
6 | process.env.EPICSHOP_CONTEXT_CWD = resolve(__dirname, '..')
7 |
8 | setupInBrowserTests()
9 |
--------------------------------------------------------------------------------
/exercises/03.using-jsx/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Using JSX
2 |
3 |
4 |
5 | 👨💼 Hooray! You now know how to use JSX effectively to make really nicely
6 | composable user interfaces with React. You've got the fundamentals down, from
7 | here it's just a matter of practice. Great job!
8 |
--------------------------------------------------------------------------------
/exercises/01.js-hello-world/01.solution.hello/README.mdx:
--------------------------------------------------------------------------------
1 | # Hello World in JS
2 |
3 |
4 |
5 | 👨💼 Awesome job. Now you know how to create DOM nodes using the regular JS APIs.
6 |
7 | But you know what, we could probably do even more in JS... Let's look at that
8 | next.
9 |
--------------------------------------------------------------------------------
/exercises/02.raw-react/01.solution.elements/README.mdx:
--------------------------------------------------------------------------------
1 | # Create React Elements
2 |
3 |
4 |
5 | 👨💼 Great work! We're well on our way to using React for building UIs on the web!
6 | But most apps are a little more complicated than a single element. Let's go
7 | deeper!
8 |
--------------------------------------------------------------------------------
/epicshop/update-deps.sh:
--------------------------------------------------------------------------------
1 | set -e
2 |
3 | npx npm-check-updates --dep prod,dev --upgrade --root
4 | cd epicshop && npx npm-check-updates --dep prod,dev --upgrade --root
5 | cd ..
6 | rm -rf node_modules package-lock.json ./epicshop/package-lock.json ./epicshop/node_modules ./exercises/**/node_modules
7 | npm install
8 | npm run setup
9 | npm run typecheck
10 | npm run lint -- --fix
11 |
--------------------------------------------------------------------------------
/exercises/03.using-jsx/02.solution.interpolation/README.mdx:
--------------------------------------------------------------------------------
1 | # Interpolation
2 |
3 |
4 |
5 | 👨💼 Great job! Remember, if you can compile the JSX you see into JavaScript
6 | expressions (and vice-versa), you'll be much more efficient with this syntax.
7 | It's all just JavaScript expressions!
8 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import defaultConfig from '@epic-web/config/eslint'
2 |
3 | /** @type {import("eslint").Linter.Config} */
4 | export default [
5 | { ignores: ['**/babel-standalone.js'] },
6 | ...defaultConfig,
7 | {
8 | rules: {
9 | // we leave unused vars around for the exercises
10 | 'no-unused-vars': 'off',
11 | '@typescript-eslint/no-unused-vars': 'off',
12 | },
13 | },
14 | ]
15 |
--------------------------------------------------------------------------------
/exercises/03.using-jsx/03.solution.spread/README.mdx:
--------------------------------------------------------------------------------
1 | # Spread props
2 |
3 |
4 |
5 | 👨💼 Great! We do this all the time in React. You'll often find yourself wrapping
6 | components to compose things in interesting ways and there's where this pattern
7 | is most useful. We'll get to custom components like that later.
8 |
--------------------------------------------------------------------------------
/exercises/01.js-hello-world/01.solution.hello/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/epicshop/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "module",
3 | "scripts": {
4 | "test:setup": "playwright install chromium --with-deps",
5 | "test": "playwright test"
6 | },
7 | "dependencies": {
8 | "@epic-web/config": "^1.21.0",
9 | "@epic-web/workshop-app": "^6.47.6",
10 | "@epic-web/workshop-utils": "^6.47.6",
11 | "epicshop": "^6.47.6",
12 | "execa": "^9.6.0",
13 | "fs-extra": "^11.3.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/exercises/01.js-hello-world/02.problem.root/README.mdx:
--------------------------------------------------------------------------------
1 | # Generate the Root Node
2 |
3 |
4 |
5 | Rather than having the `root` node in the HTML, see if you can create that one
6 | using JavaScript as well. Remove the `` from the HTML and
7 | instead of trying to find it with `document.getElementById('root')`, create it
8 | and append it to the `document.body`.
9 |
--------------------------------------------------------------------------------
/exercises/04.components/FINISHED.mdx:
--------------------------------------------------------------------------------
1 | # Custom Components
2 |
3 |
4 |
5 | 👨💼 Great job! Now you've got a good handle on what it takes to create a custom
6 | component with React. And it's not a surface-level understanding either. You
7 | understand how it works under the hood which will be critical to your experience
8 | building custom components going forward. Great job!
9 |
--------------------------------------------------------------------------------
/exercises/02.raw-react/03.problem.deep-nesting/README.mdx:
--------------------------------------------------------------------------------
1 | # Deep Nesting Elements
2 |
3 |
4 |
5 | 👨💼 Just to make sure we really understand how to nest this stuff. Try to create
6 | this using React's APIs:
7 |
8 | ```html
9 |
10 |
Here's Sam's favorite food:
11 |
12 | - Green eggs
13 | - Ham
14 |
15 |
16 | ```
17 |
--------------------------------------------------------------------------------
/exercises/04.components/02.solution.raw/README.mdx:
--------------------------------------------------------------------------------
1 | # Raw API
2 |
3 |
4 |
5 | 👨💼 Great! While that's not really all that much nicer to work with, it does get
6 | us a centimeter closer to our goal of using JSX with custom components. It's
7 | also interesting to throw a couple console logs around and see how using
8 | `createElement` compares to simply calling the function directly in our
9 | JSX.
10 |
11 | Now we're finally ready to use JSX for our custom components! Let's go!
12 |
--------------------------------------------------------------------------------
/exercises/01.js-hello-world/02.solution.root/README.mdx:
--------------------------------------------------------------------------------
1 | # Generate the Root Node
2 |
3 |
4 |
5 | 👨💼 Great! Now we can create DOM nodes dynamically using JavaScript. This is only
6 | the beginning of our journey. Creating DOM nodes ourselves is not typically the
7 | best way to build a full fledged application, but it's important for you to
8 | understand that what libraries like React are doing is not magic. They're just
9 | creating and modifying DOM nodes using JavaScript.
10 |
--------------------------------------------------------------------------------
/exercises/01.js-hello-world/02.solution.root/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/exercises/04.components/01.solution.function/README.mdx:
--------------------------------------------------------------------------------
1 | # Simple Function
2 |
3 |
4 |
5 | 👨💼 Super, but that's definitely not the most fun way to use custom components.
6 | In fact, we're technically not creating a custom component at all. We're just
7 | interpolating a function call into our template. This will have interesting
8 | implications in the future with React features like state and effects.
9 |
10 | Instead, let's create React elements out of this function which is what we're
11 | going for...
12 |
--------------------------------------------------------------------------------
/exercises/04.components/02.solution.raw/index.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, testStep } from '@epic-web/workshop-utils/test'
2 |
3 | const response = await fetch(location.href)
4 | const indexHtml = await response.text()
5 | const node = document.createElement('div')
6 | node.innerHTML = indexHtml
7 |
8 | const inlineScript = node.querySelector('script[type="text/babel"]')
9 | if (!inlineScript) {
10 | throw new Error('inlineScript not found')
11 | }
12 |
13 | await testStep('message function is passed to createElement', async () => {
14 | expect(inlineScript.textContent).to.include('createElement(message')
15 | })
16 |
--------------------------------------------------------------------------------
/exercises/03.using-jsx/03.solution.spread/index.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, testStep } from '@epic-web/workshop-utils/test'
2 |
3 | const response = await fetch(location.href)
4 | const indexHtml = await response.text()
5 | const node = document.createElement('div')
6 | node.innerHTML = indexHtml
7 |
8 | const inlineScript = node.querySelector('script[type="text/babel"]')
9 | if (!inlineScript) {
10 | throw new Error('inlineScript not found')
11 | }
12 |
13 | await testStep('props are spread on the div', async () => {
14 | expect(inlineScript.textContent, 'props should be spread').to.include(
15 | '...props',
16 | )
17 | })
18 |
--------------------------------------------------------------------------------
/exercises/04.components/03.solution.jsx/index.test.ts:
--------------------------------------------------------------------------------
1 | import { expect, testStep } from '@epic-web/workshop-utils/test'
2 |
3 | const response = await fetch(location.href)
4 | const indexHtml = await response.text()
5 | const node = document.createElement('div')
6 | node.innerHTML = indexHtml
7 |
8 | const inlineScript = node.querySelector('script[type="text/babel"]')
9 | if (!inlineScript) {
10 | throw new Error('inlineScript not found')
11 | }
12 |
13 | await testStep(
14 | 'Correctly creating a component with JSX',
15 | async () => {
16 | expect(inlineScript.textContent).to.include('
2 |
3 |
4 |
5 |
18 |
19 |
20 |
21 |
22 |