react.dev
react.dev copied to clipboard
Editor: Add hover tooltips, intelligent completions, auto formatting, diagnostics
This PR adds the following features based on tsserver to the Sandpack code editor.
-
Hover tooltips. These are styled similar to VS Code, and follow the Sandpack editor's syntax theme.
-
Format the current file: cmd-s, Shift-Alt-f
-
Tab completion, including auto-imports. For example, typing
useSta<tab>
should importuseState
and insert the rest of the word. -
Diagnostics such as type errors as well as warnings like "unused variable".
https://user-images.githubusercontent.com/296279/171644905-32963bca-ad79-45e4-8ae3-d2a93549bc30.mp4
This work is based on my improvements to @danilowoz's https://github.com/danilowoz/sandpack-tsserver
Implementation notes
We run tsserver in a Web Worker to avoid blocking the UI thread when asking for completions, etc. On every editor change, we send the new file state to the tsserver worker, and then ask for completions & linting at a throttled rate. Calls between the worker thread and UI thread are type-safe at the expense of some tricky types in ChannelBridge
.
The type data used by tsserver is automatically downloaded from a Sandpack CDN, and are cached by the UI thread in localStorage
. Dependencies in the web worker are loaded using importScripts
from unpkg.com
instead of bundled by Next, although I'm not sure this is the best call.
The codemirror plugin code that runs on the UI thread is factored into sub-extensions, so it should be able to disable unwanted features easily.
Mechanics TODOs
- [x] ๐ Currently completion is cleared whenever Sandpack reloads to show a build error. This feels very buggy and annoying. I attempted to fix this with
React.memo
, but that didn't help after I rebased.ยฏ\_(ใ)_/ยฏ
need to figure this out.- Appears to be a Sandpack issue with
showInlineErrors
, see comment below.
- Appears to be a Sandpack issue with
- [x] Test production build. Check for code size regressions, etc.
- [x] Evaluate normal dependency imports instead of
importScript(...unpkg...)
- This seems fine!
- [x] Evaluate sharing a single worker thread between all editors, which could reduce resource use.
- This is complicated, but could work by creating a new
TSServerWorker
instance per editor and connecting the editor to a specific instance using the MessageChannel API. Each editor would get a reference to the global worker (react context?), create a channel, and then callglobalWorker.makeNewEditor(channel)
. Could do as a follow-up or if maintainers feel strongly about it. - [x] Make sure we actually need a worker??
- Yep, it's janky without a worker.
- This is complicated, but could work by creating a new
- [x] Figure out how to draw tooltips over the sandbox iframe.
- [x] ๐ฎ Remove debugging statements and
debug
NPM dependency. Currently this branch spews colorful "jake.tl" messages into the console. (I will do this after all other tasks because the debugging is very helpful) - [x] Evaluate pre-packaging common type definitions to avoid dynamic resolution from 3rd party CDN. Like, we know we want @types/[email protected]. We should resolve the file at or before build time instead of doing it once per editor on app boot.
- [x] Fix ts-libs cache: sore back the cache from ts-libs into localStorage.
UX/Policy TODOs
- [x] Tooltips, but ideally it would just be a sentence
- [x] A link to the beta docs, overriding the existing React typings that use the production docs.
- [x] Autocomplete shouldn't include a myriad of global browser APIs. They're not useful in 99.9% cases here.
- [x] tune linting
- [x] Import/export linting
- [x] Remove "unused variable" check.
- [x] Remove most type errors because most docs don't typecheck
- [x] Automatic formatting, but not on "save". It's not obvious "save" even works here. Maybe on Enter? It should preserve the cursor position.
@danilowoz showInlineErrors={true}
causes autocompletion to dismiss when the line first detects an error. I haven't dug into why yet.
With showInlineErrors
:
https://user-images.githubusercontent.com/296279/171656095-8e891073-4b8a-4679-b6fe-72867da176fd.mp4
Removing that prop resolves the issue.
Hey Great work! Would you like to fix the TS errors? So that the previews are built?
@harish-sethuraman yep.... been dealing with plane wifi preventing me from pushing ๐ฑ turns out https git push works though.
Size Changes
๐ฆ Next.js Bundle Analysis
This analysis was generated by the next.js bundle analysis action ๐ค
โ ๏ธ Global Bundle Size Increased
Page | Size (compressed) |
---|---|
global |
105.5 KB (๐ด +20.84 KB) |
Details
The global bundle is the javascript bundle that loads alongside every page. It is in its own category because its impact is much higher - an increase to its size means that every page on your website loads slower, and a decrease means every page loads faster.
Any third party scripts you have added directly to your app using the <script>
tag are not accounted for in this analysis
If you want further insight into what is behind the changes, give @next/bundle-analyzer a try!
One Hundred Eighty-nine Pages Changed Size
The following pages changed size from the code in this PR compared to its base branch:
Page | Size (compressed) | First Load |
---|---|---|
/ |
47.99 KB (๐ก +28 B) |
153.5 KB |
/404 |
47.09 KB (๐ก +28 B) |
152.6 KB |
/apis |
47.88 KB (๐ก +28 B) |
153.38 KB |
/apis/createcontext |
49.63 KB (๐ก +28 B) |
155.13 KB |
/apis/reactdom |
47.82 KB (๐ก +28 B) |
153.32 KB |
/apis/render |
49.86 KB (๐ก +28 B) |
155.36 KB |
/apis/usecontext |
55.58 KB (๐ก +28 B) |
161.09 KB |
/apis/usereducer |
55.22 KB (๐ก +28 B) |
160.72 KB |
/apis/useref |
52.91 KB (๐ก +28 B) |
158.42 KB |
/apis/usestate |
58.86 KB (๐ก +35 B) |
164.37 KB |
/blog/2013/06/02/jsfiddle-integration |
57.9 KB (๐ก +28 B) |
163.41 KB |
/blog/2013/06/05/why-react |
59.63 KB (๐ก +28 B) |
165.14 KB |
/blog/2013/06/12/community-roundup |
59.33 KB (๐ก +28 B) |
164.84 KB |
/blog/2013/06/19/community-roundup-2 |
60 KB (๐ก +28 B) |
165.5 KB |
/blog/2013/06/21/react-v0-3-3 |
58.05 KB (๐ก +28 B) |
163.55 KB |
/blog/2013/06/27/community-roundup-3 |
60.37 KB (๐ก +28 B) |
165.87 KB |
/blog/2013/07/02/react-v0-4-autobind-by-default |
58.67 KB (๐ก +28 B) |
164.17 KB |
/blog/2013/07/03/community-roundup-4 |
59.74 KB (๐ก +28 B) |
165.24 KB |
/blog/2013/07/11/react-v0-4-prop-validation-and-default-values |
58.54 KB (๐ก +28 B) |
164.04 KB |
/blog/2013/07/17/react-v0-4-0 |
59.25 KB (๐ก +28 B) |
164.76 KB |
/blog/2013/07/23/community-roundup-5 |
60.13 KB (๐ก +28 B) |
165.63 KB |
/blog/2013/07/26/react-v0-4-1 |
58.02 KB (๐ก +28 B) |
163.52 KB |
/blog/2013/07/30/use-react-and-jsx-in-ruby-on-rails |
58.62 KB (๐ก +28 B) |
164.12 KB |
/blog/2013/08/05/community-roundup-6 |
59.43 KB (๐ก +28 B) |
164.93 KB |
/blog/2013/08/19/use-react-and-jsx-in-python-applications |
58.63 KB (๐ก +28 B) |
164.14 KB |
/blog/2013/08/26/community-roundup-7 |
59.59 KB (๐ก +28 B) |
165.1 KB |
/blog/2013/09/24/community-roundup-8 |
61.03 KB (๐ก +28 B) |
166.53 KB |
/blog/2013/10/03/community-roundup-9 |
59.96 KB (๐ก +28 B) |
165.46 KB |
/blog/2013/10/16/react-v0.5.0 |
59.65 KB (๐ก +28 B) |
165.15 KB |
/blog/2013/10/29/react-v0-5-1 |
58 KB (๐ก +28 B) |
163.51 KB |
/blog/2013/11/06/community-roundup-10 |
61.62 KB (๐ก +28 B) |
167.13 KB |
/blog/2013/11/18/community-roundup-11 |
60.7 KB (๐ก +28 B) |
166.21 KB |
/blog/2013/12/18/react-v0.5.2-v0.4.2 |
58.48 KB (๐ก +28 B) |
163.98 KB |
/blog/2013/12/19/react-v0.8.0 |
58.9 KB (๐ก +28 B) |
164.4 KB |
/blog/2013/12/23/community-roundup-12 |
60.36 KB (๐ก +28 B) |
165.86 KB |
/blog/2013/12/30/community-roundup-13 |
60.14 KB (๐ก +28 B) |
165.64 KB |
/blog/2014/01/02/react-chrome-developer-tools |
58.4 KB (๐ก +28 B) |
163.9 KB |
/blog/2014/01/06/community-roundup-14 |
59.73 KB (๐ก +28 B) |
165.24 KB |
/blog/2014/02/05/community-roundup-15 |
60.95 KB (๐ก +28 B) |
166.46 KB |
/blog/2014/02/15/community-roundup-16 |
60.66 KB (๐ก +28 B) |
166.16 KB |
/blog/2014/02/16/react-v0.9-rc1 |
61.07 KB (๐ก +28 B) |
166.57 KB |
/blog/2014/02/20/react-v0.9 |
61.51 KB (๐ก +28 B) |
167.02 KB |
/blog/2014/02/24/community-roundup-17 |
60.38 KB (๐ก +28 B) |
165.88 KB |
/blog/2014/03/14/community-roundup-18 |
61.34 KB (๐ก +28 B) |
166.85 KB |
/blog/2014/03/19/react-v0.10-rc1 |
59.65 KB (๐ก +28 B) |
165.16 KB |
/blog/2014/03/21/react-v0.10 |
59.67 KB (๐ก +28 B) |
165.18 KB |
/blog/2014/03/28/the-road-to-1.0 |
59.79 KB (๐ก +28 B) |
165.29 KB |
/blog/2014/04/04/reactnet |
58.43 KB (๐ก +28 B) |
163.93 KB |
/blog/2014/05/06/flux |
58.55 KB (๐ก +28 B) |
164.06 KB |
/blog/2014/05/29/one-year-of-open-source-react |
58.78 KB (๐ก +28 B) |
164.28 KB |
/blog/2014/06/27/community-roundup-19 |
60.32 KB (๐ก +28 B) |
165.83 KB |
/blog/2014/07/13/react-v0.11-rc1 |
60.71 KB (๐ก +28 B) |
166.21 KB |
/blog/2014/07/17/react-v0.11 |
61.96 KB (๐ก +28 B) |
167.46 KB |
/blog/2014/07/25/react-v0.11.1 |
58.91 KB (๐ก +28 B) |
164.41 KB |
/blog/2014/07/28/community-roundup-20 |
60.46 KB (๐ก +28 B) |
165.97 KB |
/blog/2014/07/30/flux-actions-and-the-dispatcher |
60.1 KB (๐ก +28 B) |
165.6 KB |
/blog/2014/08/03/community-roundup-21 |
60.13 KB (๐ก +28 B) |
165.64 KB |
/blog/2014/09/03/introducing-the-jsx-specification |
58.14 KB (๐ก +28 B) |
163.64 KB |
/blog/2014/09/12/community-round-up-22 |
60.61 KB (๐ก +28 B) |
166.12 KB |
/blog/2014/09/16/react-v0.11.2 |
58.91 KB (๐ก +28 B) |
164.41 KB |
/blog/2014/09/24/testing-flux-applications |
62.09 KB (๐ก +28 B) |
167.59 KB |
/blog/2014/10/14/introducing-react-elements |
61.04 KB (๐ก +28 B) |
166.55 KB |
/blog/2014/10/16/react-v0.12-rc1 |
61.01 KB (๐ก +28 B) |
166.51 KB |
/blog/2014/10/17/community-roundup-23 |
61.58 KB (๐ก +28 B) |
167.08 KB |
/blog/2014/10/27/react-js-conf |
58.18 KB (๐ก +28 B) |
163.69 KB |
/blog/2014/10/28/react-v0.12 |
60.95 KB (๐ก +28 B) |
166.45 KB |
/blog/2014/11/24/react-js-conf-updates |
58.57 KB (๐ก +28 B) |
164.07 KB |
/blog/2014/11/25/community-roundup-24 |
61.81 KB (๐ก +28 B) |
167.31 KB |
/blog/2014/12/18/react-v0.12.2 |
58.6 KB (๐ก +28 B) |
164.1 KB |
/blog/2014/12/19/react-js-conf-diversity-scholarship |
58.99 KB (๐ก +28 B) |
164.5 KB |
/blog/2015/01/27/react-v0.13.0-beta-1 |
59.96 KB (๐ก +28 B) |
165.46 KB |
/blog/2015/02/18/react-conf-roundup-2015 |
61.94 KB (๐ก +28 B) |
167.45 KB |
/blog/2015/02/20/introducing-relay-and-graphql |
61.1 KB (๐ก +28 B) |
166.6 KB |
/blog/2015/02/24/react-v0.13-rc1 |
60 KB (๐ก +28 B) |
165.5 KB |
/blog/2015/02/24/streamlining-react-elements |
62.55 KB (๐ก +28 B) |
168.05 KB |
/blog/2015/03/03/react-v0.13-rc2 |
59.33 KB (๐ก +28 B) |
164.84 KB |
/blog/2015/03/04/community-roundup-25 |
60.29 KB (๐ก +28 B) |
165.8 KB |
/blog/2015/03/10/react-v0.13 |
60.61 KB (๐ก +28 B) |
166.11 KB |
/blog/2015/03/16/react-v0.13.1 |
58.43 KB (๐ก +28 B) |
163.94 KB |
/blog/2015/03/19/building-the-facebook-news-feed-with-relay |
61.19 KB (๐ก +28 B) |
166.69 KB |
/blog/2015/03/26/introducing-react-native |
58.33 KB (๐ก +28 B) |
163.83 KB |
/blog/2015/03/30/community-roundup-26 |
60.26 KB (๐ก +28 B) |
165.76 KB |
/blog/2015/04/17/react-native-v0.4 |
59.17 KB (๐ก +28 B) |
164.68 KB |
/blog/2015/04/18/react-v0.13.2 |
58.51 KB (๐ก +28 B) |
164.01 KB |
/blog/2015/05/01/graphql-introduction |
63 KB (๐ก +28 B) |
168.5 KB |
/blog/2015/05/08/react-v0.13.3 |
58.4 KB (๐ก +28 B) |
163.9 KB |
/blog/2015/05/22/react-native-release-process |
58.55 KB (๐ก +28 B) |
164.05 KB |
/blog/2015/06/12/deprecating-jstransform-and-react-tools |
59 KB (๐ก +28 B) |
164.5 KB |
/blog/2015/07/03/react-v0.14-beta-1 |
60.51 KB (๐ก +28 B) |
166.01 KB |
/blog/2015/08/03/new-react-devtools-beta |
59.11 KB (๐ก +28 B) |
164.62 KB |
/blog/2015/08/11/relay-technical-preview |
59.18 KB (๐ก +28 B) |
164.68 KB |
/blog/2015/08/13/reacteurope-roundup |
60.76 KB (๐ก +28 B) |
166.27 KB |
/blog/2015/09/02/new-react-developer-tools |
58.53 KB (๐ก +28 B) |
164.03 KB |
/blog/2015/09/10/react-v0.14-rc1 |
64.22 KB (๐ก +28 B) |
169.72 KB |
/blog/2015/09/14/community-roundup-27 |
60.69 KB (๐ก +28 B) |
166.19 KB |
/blog/2015/10/01/react-render-and-top-level-api |
59.66 KB (๐ก +28 B) |
165.17 KB |
/blog/2015/10/07/react-v0.14 |
64.7 KB (๐ก +28 B) |
170.2 KB |
/blog/2015/10/19/reactiflux-is-moving-to-discord |
60.41 KB (๐ก +28 B) |
165.92 KB |
/blog/2015/10/28/react-v0.14.1 |
58.42 KB (๐ก +28 B) |
163.93 KB |
/blog/2015/11/02/react-v0.14.2 |
58.45 KB (๐ก +28 B) |
163.96 KB |
/blog/2015/11/18/react-v0.14.3 |
58.57 KB (๐ก +28 B) |
164.08 KB |
/blog/2015/12/04/react-js-conf-2016-diversity-scholarship |
59.45 KB (๐ก +28 B) |
164.95 KB |
/blog/2015/12/16/ismounted-antipattern |
59.05 KB (๐ก +28 B) |
164.55 KB |
/blog/2015/12/18/react-components-elements-and-instances |
62.97 KB (๐ก +28 B) |
168.47 KB |
/blog/2015/12/29/react-v0.14.4 |
58.24 KB (๐ก +28 B) |
163.75 KB |
/blog/2016/01/08/A-implies-B-does-not-imply-B-implies-A |
59.26 KB (๐ก +28 B) |
164.76 KB |
/blog/2016/01/12/discontinuing-ie8-support |
58.07 KB (๐ก +28 B) |
163.58 KB |
/blog/2016/02/19/new-versioning-scheme |
59.45 KB (๐ก +28 B) |
164.95 KB |
/blog/2016/03/07/react-v15-rc1 |
62.2 KB (๐ก +28 B) |
167.71 KB |
/blog/2016/03/16/react-v15-rc2 |
59.07 KB (๐ก +28 B) |
164.57 KB |
/blog/2016/03/29/react-v0.14.8 |
58.2 KB (๐ก +28 B) |
163.71 KB |
/blog/2016/04/07/react-v15 |
66.39 KB (๐ก +28 B) |
171.89 KB |
/blog/2016/04/08/react-v15.0.1 |
58.96 KB (๐ก +28 B) |
164.46 KB |
/blog/2016/07/11/introducing-reacts-error-code-system |
58.67 KB (๐ก +28 B) |
164.17 KB |
/blog/2016/07/13/mixins-considered-harmful |
66.82 KB (๐ก +28 B) |
172.32 KB |
/blog/2016/07/22/create-apps-with-no-configuration |
62.15 KB (๐ก +28 B) |
167.65 KB |
/blog/2016/08/05/relay-state-of-the-state |
62.19 KB (๐ก +28 B) |
167.69 KB |
/blog/2016/09/28/our-first-50000-stars |
63.64 KB (๐ก +28 B) |
169.14 KB |
/blog/2016/11/16/react-v15.4.0 |
61.59 KB (๐ก +28 B) |
167.09 KB |
/blog/2017/04/07/react-v15.5.0 |
62.15 KB (๐ก +28 B) |
167.66 KB |
/blog/2017/05/18/whats-new-in-create-react-app |
61.55 KB (๐ก +28 B) |
167.05 KB |
/blog/2017/06/13/react-v15.6.0 |
60.17 KB (๐ก +28 B) |
165.68 KB |
/blog/2017/07/26/error-handling-in-react-16 |
60.52 KB (๐ก +28 B) |
166.03 KB |
/blog/2017/09/08/dom-attributes-in-react-16 |
60.95 KB (๐ก +28 B) |
166.46 KB |
/blog/2017/09/25/react-v15.6.2 |
59.28 KB (๐ก +28 B) |
164.79 KB |
/blog/2017/09/26/react-v16.0 |
64.42 KB (๐ก +28 B) |
169.92 KB |
/blog/2017/11/28/react-v16.2.0-fragment-support |
62.35 KB (๐ก +28 B) |
167.85 KB |
/blog/2017/12/07/introducing-the-react-rfc-process |
58.87 KB (๐ก +28 B) |
164.37 KB |
/blog/2017/12/15/improving-the-repository-infrastructure |
74.66 KB (๐ก +28 B) |
180.16 KB |
/blog/2018/03/01/sneak-peek-beyond-react-16 |
58.83 KB (๐ก +28 B) |
164.33 KB |
/blog/2018/03/27/update-on-async-rendering |
64.07 KB (๐ก +28 B) |
169.57 KB |
/blog/2018/03/29/react-v-16-3 |
60.89 KB (๐ก +28 B) |
166.39 KB |
/blog/2018/05/23/react-v-16-4 |
61.03 KB (๐ก +28 B) |
166.53 KB |
/blog/2018/06/07/you-probably-dont-need-derived-state |
64.69 KB (๐ก +28 B) |
170.19 KB |
/blog/2018/08/01/react-v-16-4-2 |
59.68 KB (๐ก +28 B) |
165.18 KB |
/blog/2018/09/10/introducing-the-react-profiler |
61.29 KB (๐ก +28 B) |
166.79 KB |
/blog/2018/10/01/create-react-app-v2 |
61.76 KB (๐ก +28 B) |
167.27 KB |
/blog/2018/10/23/react-v-16-6 |
60.96 KB (๐ก +28 B) |
166.47 KB |
/blog/2018/11/13/react-conf-recap |
58.68 KB (๐ก +28 B) |
164.18 KB |
/blog/2018/11/27/react-16-roadmap |
64.87 KB (๐ก +28 B) |
170.37 KB |
/blog/2018/12/19/react-v-16-7 |
59.8 KB (๐ก +28 B) |
165.3 KB |
/blog/2019/02/06/react-v16.8.0 |
62.07 KB (๐ก +28 B) |
167.57 KB |
/blog/2019/02/23/is-react-translated-yet |
61.63 KB (๐ก +28 B) |
167.13 KB |
/blog/2019/08/08/react-v16.9.0 |
64.56 KB (๐ก +28 B) |
170.06 KB |
/blog/2019/08/15/new-react-devtools |
59.17 KB (๐ก +28 B) |
164.67 KB |
/blog/2019/10/22/react-release-channels |
60.99 KB (๐ก +28 B) |
166.49 KB |
/blog/2019/11/06/building-great-user-experiences-with-concurrent-mode-and-suspense |
65.68 KB (๐ก +28 B) |
171.19 KB |
/blog/2020/02/26/react-v16.13.0 |
61.91 KB (๐ก +28 B) |
167.41 KB |
/blog/2020/08/10/react-v17-rc |
68.19 KB (๐ก +28 B) |
173.69 KB |
/community |
47.69 KB (๐ก +28 B) |
153.19 KB |
/community/acknowledgements |
48.87 KB (๐ก +28 B) |
154.38 KB |
/community/meet-the-team |
49.57 KB (๐ก +28 B) |
155.08 KB |
/learn |
52.65 KB (๐ก +28 B) |
158.15 KB |
/learn/add-react-to-a-website |
52.76 KB (๐ก +28 B) |
158.27 KB |
/learn/adding-interactivity |
54.33 KB (๐ก +28 B) |
159.84 KB |
/learn/choosing-the-state-structure |
60.05 KB (๐ก +28 B) |
165.56 KB |
/learn/conditional-rendering |
52.28 KB (๐ก +28 B) |
157.79 KB |
/learn/describing-the-ui |
51.5 KB (๐ก +28 B) |
157 KB |
/learn/editor-setup |
48.96 KB (๐ก +28 B) |
154.47 KB |
/learn/escape-hatches |
47.01 KB (๐ก +28 B) |
152.52 KB |
/learn/extracting-state-logic-into-a-reducer |
57.69 KB (๐ก +28 B) |
163.19 KB |
/learn/importing-and-exporting-components |
50.68 KB (๐ก +28 B) |
156.19 KB |
/learn/installation |
48.29 KB (๐ก +28 B) |
153.79 KB |
/learn/javascript-in-jsx-with-curly-braces |
50.76 KB (๐ก +28 B) |
156.26 KB |
/learn/keeping-components-pure |
54.89 KB (๐ก +28 B) |
160.39 KB |
/learn/managing-state |
53.61 KB (๐ก +28 B) |
159.11 KB |
/learn/manipulating-the-dom-with-refs |
55.86 KB (๐ก +28 B) |
161.36 KB |
/learn/passing-data-deeply-with-context |
55.41 KB (๐ก +28 B) |
160.92 KB |
/learn/passing-props-to-a-component |
54.46 KB (๐ก +28 B) |
159.96 KB |
/learn/preserving-and-resetting-state |
58.43 KB (๐ก +28 B) |
163.93 KB |
/learn/queueing-a-series-of-state-updates |
52.25 KB (๐ก +28 B) |
157.76 KB |
/learn/react-developer-tools |
48.25 KB (๐ก +28 B) |
153.75 KB |
/learn/reacting-to-input-with-state |
56.87 KB (๐ก +28 B) |
162.37 KB |
/learn/referencing-values-with-refs |
53.77 KB (๐ก +28 B) |
159.28 KB |
/learn/render-and-commit |
50.75 KB (๐ก +28 B) |
156.26 KB |
/learn/rendering-lists |
54.84 KB (๐ก +28 B) |
160.35 KB |
/learn/responding-to-events |
53.78 KB (๐ก +28 B) |
159.28 KB |
/learn/scaling-up-with-reducer-and-context |
51.82 KB (๐ก +28 B) |
157.33 KB |
/learn/sharing-state-between-components |
52.9 KB (๐ก +28 B) |
158.41 KB |
/learn/start-a-new-react-project |
49.32 KB (๐ก +28 B) |
154.83 KB |
/learn/state-a-components-memory |
57.98 KB (๐ก +28 B) |
163.48 KB |
/learn/state-as-a-snapshot |
51.56 KB (๐ก +28 B) |
157.06 KB |
/learn/synchronizing-with-effects |
64.63 KB (๐ก +28 B) |
170.13 KB |
/learn/thinking-in-react |
53.62 KB (๐ก +28 B) |
159.12 KB |
/learn/updating-arrays-in-state |
55.85 KB (๐ก +28 B) |
161.36 KB |
/learn/updating-objects-in-state |
55.58 KB (๐ก +28 B) |
161.09 KB |
/learn/writing-markup-with-jsx |
51 KB (๐ก +28 B) |
156.51 KB |
/learn/you-might-not-need-an-effect |
62.68 KB (๐ก +28 B) |
168.18 KB |
/learn/your-first-component |
51.59 KB (๐ก +28 B) |
157.09 KB |
Details
Only the gzipped size is provided here based on an expert tip.
First Load is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If next/link
is used, subsequent page loads would only need to download that page's bundle (the number in the "Size" column), since the global bundle has already been downloaded.
Any third party scripts you have added directly to your app using the <script>
tag are not accounted for in this analysis
Next to the size is how much the size has increased or decreased compared with the base branch of this PR. If this percentage has increased by 10% or more, there will be a red status indicator applied, indicating that special attention should be given to this.
https://github.com/reactjs/reactjs.org/pull/4307/ - Can I merge this as part of your PR? This enables TSX template in sandpack. Or you can use the same in your PR if needed.
@harish-sethuraman
https://github.com/reactjs/reactjs.org/pull/4307 - Can I merge this as part of your PR?
I prefer not to expand the scope of this PR any further. It seems fine if you merge your change afterwards.
@danilowoz
currently, the bundler is not able to handle the TS types because we need to tell it to deal with TS types properly
Why is this needed? I havenโt noticed the bundler breaking while using the editing features added here. My goal here is to bring autocomplete etc to the existing JS documentation, not to change all the documentation to use typescript.
If desired, we could do this in a follow-up PR unless Iโm mistaken.
My main concern with this is it feels very TS-centric (for obvious reasons). E.g. you hover over <button>
and see (โpropertyโ) JSX.IntrinsicElements.button: React.DetailedHTMLProps<โReact.ButtonHTMLAttributes<โHTMLButtonElementโ>, HTMLButtonElementโ>
. This is overwhelming and not particularly useful (IMO) to people who aren't TS experts.
Do you share this concern?
Yeah, I want to strike a good balance for this integration to provide meaningful help to React learners without overloading them with a second subject to study (Typescript).
I think we have three jobs to do in this PR:
- Figure out if this effort is worth-while. If there's not enough promise in these features to warrant effort to iterate, it's fine to close.
- Review/iterate on the mechanics. Is performance impact acceptable? Is code too complex to accept in this project? Is the architecture okay?
- Review/iterate on the UX & policy. Currently I've cloned these features from VS Code, plus rounded corners. We should refine it further for the learner use-case.
I think the hover tooltip can be very helpful to learners if it surfaces understandable or actionable information, and harmful in cases like the <button>
type. Even for a Typescript expert (?me?), this hover tip isn't actionable. Looking at this, all I know is "it's some kind of react thing related to HTMLButtonElement"; and the only action I can take when presented with complex types like this is to go read the type definition using "Go to type definition", which we can't do in reactjs.org.
There are many ways we can adjust this feature, and other features to avoid overwhelming learners, with different tradeoffs. For example, we could hide all the type information, and only show doc comments. useState
and other hooks have succinct descriptions; showing doc comments avoids students needing a second window/tab to look at reference material in many cases. The downside is that we miss out on type hovers that are understandable to a large degree such as primitive types or JSX property names.
I think ideally I'd like to see:
- Automatic formatting, but not on "save". It's not obvious "save" even works here. Maybe on Enter? It should preserve the cursor position.
- Auto-import but you shouldn't need to know to press cmd+space. It should "notice" when you're typing useState, for example.
- Tooltips, but ideally it would just be a sentence + the link should somehow go to the beta docs. (Can we override it?)
- Autocomplete shouldn't include a myriad of global browser APIs. They're not useful in 99.9% cases here.
- Import/export linting is good. I don't think we need the "unused variable" check though.
Hover & imports without types:


Without @see
, @version
. It looks lonely to me since I'm used to VS Code, but removes even more clutter.


version does seem useless to me... see is ok if it linked to the right thing but if we can't make it yet then it's better without it.
Would be nice if backticks turned into inline code style.
@gaearon do you have an existing plan for migrating links from the old docs URLs to the new docs? Do you have any kind of route mapping already written? If so, we could try applying the migration plan to all the URLs in TSDoc.
Otherwise, we can remove @see
, then render a link to /apis/${identifier.toLowerCase()}
if the identifier is imported from 'react' or 'react-dom'
added a quick hack to replace @see
URLs with Docs: <link to beta docs>
https://user-images.githubusercontent.com/296279/171918409-cea813e6-193a-46ee-a7f1-9ecbdc298ff9.mp4
added a quick hack to replace @see URLs with Docs:
this looks great
@danilowoz Curious why you implemented type acquisition yourself versus re-using https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ata, which is used by https://www.typescriptlang.org/play. It fetches files from jsdelivr.
First of all this is super cool, great work.
One of the reasons I didn't build it this way on the TypeScript website is because it means each code sample using this technique needs ~8mb of downloaded JS to work at runtime. That's a lot of JS for something which is structurally opt-in (e.g. you have to start interacting with the code sample for it to be useful) which has both download costs, and adds to the memory usage for each page. For example: loading up a page like https://beta-reactjs-cg21dnj16-fbopensource.vercel.app/learn/describing-the-ui triggers hundreds of JS requests (a lot are cache hits though) and at least one web worker per code sample (~10)
As this is all still early, it might be worth looking into whether you could share one tsserver across all code samples, or incrementally loading in the tsserver code on demand
@orta:
As this is all still early, it might be worth looking into whether you could share one tsserver across all code samples, or incrementally loading in the tsserver code on demand
Yeah, I agree. I lifted the worker into a provider so we only need a single worker for the lifetime of the app. The worker is created only when needed by an editor.
I tried two strategies for lazy loading:
- Load the worker as soon as an editor mounts (at https://github.com/reactjs/reactjs.org/pull/4720/commits/b0d2eb2b15cdb3cbb7cfe1b8de1e49f908686768). This is simple but works well.
- Load the worker once the user hovers or focuses an editor. This is more optimal from a resource use perspective, but has some issues. On slower connections these features feel more inconsistent since we trigger loading later.
@danilowoz Is there a better way to override Sandpack's styling of the Codemirror tooltip style than using !important
in my own extension?
Without a lot of !important, lazy-loading my plugin leads to inconsistent CSS ordering seen here:
https://user-images.githubusercontent.com/296279/172221096-b135fb13-d2f7-400a-ba3f-0c421c00a311.mp4
Okay folks! This is ready for another review pass. I believe I've completed all the mechanical and UX TODOs I noted from previous comments. Thanks for the feedback so far, keep it coming.
For now I've left config.ts
intact with the hope that the code size for the debug features/config options is worth leaving these in; and that it makes it easier for @danilowoz to extract and generalize this work if they want to do so. Happy to remove once the next review pass is done and I won't need to debug/change UX any more ๐.
Hey @justjake, here are the following up about some issues:
- Tooltip CSS override: actually, these styles come from the React Docs website and not from the Sandpack package, so that you can update it in the following file. Please take into account that it might affect the eslint tooltip too, and maybe it's worth syncing its styles in order to keep both tooltips consistent: https://github.com/justjake/reactjs.org/blob/jake--tsserver/beta/src/styles/sandpack.css#L107-L110
- The
showInlineErrors
bug has been fixed on0.19.8-experimental.6
;
Although a lot of performance improvements were made, the worker is still using a significant amount of memory (~300MB on /learn/describing-the-ui
, other pages might be less). Maybe it's because it only uses a single worker now?
Please take into account that it might affect the eslint tooltip too, and maybe it's worth syncing its styles in order to keep both tooltips consistent: https://github.com/justjake/reactjs.org/blob/jake--tsserver/beta/src/styles/sandpack.css#L107-L110
๐คฆ๐ป thanks for pointing that out!
Although a lot of performance improvements were made, the worker is still using a significant amount of memory (~300MB on /learn/describing-the-ui, other pages might be less). Maybe it's because it only uses a single worker now?
I can do some more work on lazy instantiation of the the Typescript features per editor. Right now once any editor receives interaction, all the editors boot up and create typescript environments in the shared worker. I think I can do more by lazily instantiating language services per editor only after interaction.
Typescript environments are now created on first interaction. There's an initial cost of ~60MB heap to boot the tsserver worker and create the first environment, and an additional ~30MB cost per additional environment. Numbers from my M1 Mac, Chrome 102.
Environments are dereferenced when their Sandpack editor is scrolled off-screen and returns to the "idle" state. Memory use can peak up to ~300MB, but dereferenced environments are cleaned up, so usage returns to around ~80mb on /learn/describing-the-ui if I scroll around and click on each editor to force Typescript environment (re)creation.
Hello โ I have some free time coming up that I'd like to use finishing this work. @gaearon @harish-sethuraman @danilowoz What are the blockers here that I need to resolve?
Is there an up-to-date summary of the exact features being added? Since we went back and forth a few times. There is a lot of code and I worry about how to balance the complexity being added vs the payoff. The things that seems most useful to me are autoformatting and possibility of doing TypeScript sandboxes (that works, right?) Can you show how it works with TS code?
@gaearon
JS Features
These apply to all the existing JS code examples:
- Report syntax errors and import-related errors with red squiggles. Other kinds of errors, especially type errors, are ignored.
- Intelligent type-ahead completion, including smart imports. Autocomplete doesn't suggest a bunch of random browser globals.
- Hover tooltips for tokens that have documentation.
- All tooltips (hover & import) show documentation and links to beta.reactjs.org/apis/name. Tooltips don't show Typescript syntax.
- Auto-format the file (preserving cursor position) on newline, Cmd-S (muscle memory matches CodeSandbox.com & VS Code), or Shift-Alt-F (matches VS Code)



Optimizations
- This system uses a single WebWorker, shared via React Context.
- The worker is lazily instantiated when a user interacts with a Sandpack example for the first time
- The worker creates new typescript environments for each example, and then dereferences them if the example is scrolled off-screen to conserve memory overall.
In my testing, these changes add ~55mb of Javascript heap once the user interacts with a code editor and while that editor remains on-screen. This brings total heap size from ~70mb to 125mb while editing a sandbox.
Typescript
Write App.tsx
examples in Typescript, via ```typescript code fences. This automatically enables the following features:
- Full type error reporting for Typescript code.
- Build each .tsx file to JS, and show the JS as a tab.
- The JS version stays in-sync with the TSX version until the user edits the JS version. Then, it's independent until the user presses "Reset".
- When the JS version of a file is selected, it's used in the bundle instead of the JS version.
- When the TSX version of a file is selected, it's used in the bundle instead of the JS version.
https://user-images.githubusercontent.com/296279/175845725-6fb8aa9b-5041-40c3-9042-9e791fc0b842.mp4