Open piscopancer opened 10 months ago
What information should I provide you to figure out what's causing the lag?
What version of Studio, browser, CPU etc would be helpful, thanks!
@bjoerge sanity version is 3.20.0, CPU is AMD Ryzen 5 (Cyberpunk 2077 medium graphics 40-60fps), the browser is Yandex Browser. In fact, inputs lag both on mobile (google chrome) and my laptop (yandex browser), I suppose the problem must be related to my project, not hardware.
Do you have any assumptions what can cause the input lag? Initially, I thought it happens because an input sends a request to a sanity server to write new value, and updates itself only after the server responds with updated value, in other words, with success. 🤔
"dependencies": {
"@mdx-js/loader": "^3.0.0",
"@next/mdx": "^14.0.3",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/react-tooltip": "^1.0.7",
"@sanity/ui": "^1.9.3",
"@sanity/vision": "^3.20.0",
"@studio-freight/lenis": "^1.0.27",
"@svgr/webpack": "^8.1.0",
"@types/node": "20.9.1",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
"autoprefixer": "10.4.16",
"date-fns": "^2.30.0",
"easymde": "^2.18.0",
"eslint": "8.54.0",
"eslint-config-next": "^14.0.3",
"framer-motion": "^10.16.5",
"motion": "^10.16.4",
"next": "^14.0.3",
"next-mdx-remote": "^4.4.1",
"next-sanity": "^6.0.5",
"postcss": "8.4.31",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.12.0",
"react-markdown": "^9.0.1",
"react-refractor": "^2.1.7",
"react-use-precision-timer": "^3.3.1",
"refractor": "^4.8.1",
"rehype-react": "^8.0.0",
"remark-gfm": "^4.0.0",
"remark-unwrap-images": "^4.0.0",
"sanity": "^3.20.0",
"sanity-plugin-markdown": "^4.1.0",
"sass": "^1.69.5",
"tailwindcss": "3.3.5",
"transliteration": "^2.3.5",
"typescript": "5.2.2",
"valtio": "^1.12.0"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.10",
"prettier": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.7"
}
I have the same issue with sanity in next. Here is a performance profile while I type some text. I first thought it had something to do with preview. but this happens without any preview provider enabled.
Some things I noticed:
Not sure if normal, but a single character triggers quite a lot of network calls. (1 post and 4ish get's). When I type it looks like this.
{
"mutations": [{
"createIfNotExists": {
"_id": "drafts.64a7f1c4-0f6b-42c6-8c5c-bf9eda5cbddd",
"_type": "carConfigurationPage",
"_createdAt": "2023-11-23T11:29:17Z",
"carTypes": [{
"_key": "6b853240c73e",
"_ref": "7cDM6ekVZIUb0JKOB5sk3E",
"_type": "reference"
}, {
"_key": "644c8eb221e6",
"_ref": "dbb3db34-6e66-40dd-9626-df3ae159634d",
"_type": "reference"
}],
"sections": [{
"_key": "4f18f9b04afc",
"_type": "carConfigurationSection",
"content": [{
"_key": "ab0967ff4863",
"_type": "textContent",
"content": "Intro om farge og slikt"
}, {
"_key": "73ed2c959ab4",
"_type": "carOptionsContent",
"key": "farge"
}],
"title": "Farge"
}, {
"_key": "ab6bc49f9ce2",
"_type": "carConfigurationSection",
"content": [{
"_key": "9fae8bfa67c2",
"_type": "textContent",
"content": "Intro om felger"
}, {
"_key": "11335e856cfc",
"_type": "carOptionsContent",
"key": "felger"
}, {
"_key": "fac3a54608fe",
"_type": "carOptionsContent",
"key": "vinterhjul"
}],
"title": "Felger"
}],
"title": "Born 62"
}
}, {
"patch": {
"setIfMissing": {
"sections": []
},
"id": "drafts.64a7f1c4-0f6b-42c6-8c5c-bf9eda5cbddd"
}
}, {
"patch": {
"setIfMissing": {
"sections[_key==\"ab6bc49f9ce2\"]": {
"_type": "carConfigurationSection"
}
},
"id": "drafts.64a7f1c4-0f6b-42c6-8c5c-bf9eda5cbddd"
}
}, {
"patch": {
"setIfMissing": {
"sections[_key==\"ab6bc49f9ce2\"].content": []
},
"id": "drafts.64a7f1c4-0f6b-42c6-8c5c-bf9eda5cbddd"
}
}, {
"patch": {
"setIfMissing": {
"sections[_key==\"ab6bc49f9ce2\"].content[_key==\"9fae8bfa67c2\"]": {
"_type": "textContent"
}
},
"id": "drafts.64a7f1c4-0f6b-42c6-8c5c-bf9eda5cbddd"
}
}, {
"patch": {
"id": "drafts.64a7f1c4-0f6b-42c6-8c5c-bf9eda5cbddd",
"diffMatchPatch": {
"sections[_key==\"ab6bc49f9ce2\"].content[_key==\"9fae8bfa67c2\"].content": "@@ -20,60 +20,5 @@\n s%C3%A5nt\n- her what is this about? some more text that is comming\n"
}
}
}],
"transactionId": "5da59b76-79ee-47c9-bcd9-a2db822d5f2f"
}
"dependencies": {
"@datadog/browser-logs": "^5.4.0",
"@datadog/browser-rum": "^5.4.0",
"@moller/design-system": "^6.11.0",
"@portabletext/react": "^3.0.11",
"@sanity/client": "^6.8.6",
"@sanity/image-url": "^1.0.2",
"@sanity/overlays": "^2.0.2",
"@sanity/preview-kit": "^4.0.1",
"@sanity/vision": "^3.20.1",
"@types/node": "20.9.4",
"@types/react": "18.2.38",
"@types/react-dom": "18.2.17",
"autoprefixer": "10.4.16",
"classnames": "^2.3.2",
"eslint-config-next": "14.0.3",
"groq": "^3.20.1",
"jotai": "^2.5.1",
"next": "14.0.3",
"next-sanity": "^6.0.5",
"postcss": "^8.4.31",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.12.0",
"sanity": "^3.20.1",
"sanity-plugin-iframe-pane": "^2.6.1",
"sanity-plugin-media": "^2.2.4",
"suspend-react": "^0.1.3",
"swr": "^2.2.4"
}
We landed a couple of performance optimizations in v3.20.2, which should make things significantly faster. Could you try upgrading and see if that helps?
@bjoerge installed and built it. Not much difference.
I can see that each keypress event trigger is ~45 ms long.
Video: https://github.com/sanity-io/sanity/assets/365605/6851ce41-707e-43dc-955c-47f6a9076894
@bjoerge Looks like something in react hooks into keypress and triggers on every input.
A look at reacts profiler plugin for chrome it looks like the whole sanity studio component is re-rendering on keypress
Screenshot from react profile on which component that re-renders on keypress.
Here is a video displaying the issue. The whole document pane re-renders on keypress.
https://github.com/sanity-io/sanity/assets/365605/9631adc4-952a-4013-b2d8-45daecbb3704
I created a new sanity project and added a single schema. I see a 14 ms re-render on each keypress.
import {defineField, defineType} from 'sanity'
const textContent = defineType({ name: 'textContent', title: 'Text content', type: 'document', fields: [ defineField({ name: 'name', title: 'Name', type: 'string', }), defineField({ name: 'content', title: 'Content', description: 'Text content', type: 'text', }), ], })
export const schemaTypes = [textContent]
3. Open studio, create a new document and start typing in the content text area. Run profiler og react profiler to record performance.
Hi again. After looking at the code I see that each field is memorized for performance. I see 2 issues with todays solution.
Simple string field: 1 character input
Complex object field: 1 character input
Complext object structure
defineField({
name: 'sections',
title: 'Sections',
description: 'Page sections',
type: 'array',
of: [{ type: carConfigurationSection }],
}),
export default defineType({
name: carConfigurationSection,
title: 'Section',
type: 'object',
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
}),
defineField({
name: 'content',
title: 'Content',
description: 'Section content',
type: 'array',
of: [{ type: textContent }, { type: carOptionsContent }],
}),
],
});
export default defineType({
name: textContent,
title: 'Text content',
type: 'object',
fields: [
defineField({
name: 'name',
title: 'Name',
type: 'string',
}),
defineField({
name: 'content',
title: 'Content',
description: 'Text content',
type: 'text',
}),
],
});
...
@hallatore thanks for taking the time to provide details. We are well aware of how the changed propagate from text inputs back to the UI, but it works this way for a reason. Debouncing inputs is generally something we want to avoid due to the complexity it adds (correctness trumps performance).
To be clear: I'm not disagreeing with your points, nor am I saying it shouldn't be faster, but we're a small team with lots of other priorities, so we're aiming for acceptable, not perfect when it comes to performance. I'm curious whether this is causing real issues for you or your editors in production, or if we're moving into "it would be nice if it was faster"-territory here. 14ms is comfortably above 60fps, and there's hardly anyone in this world that type at a rate that actually requires 60fps.
Also keep in mind that (as you probably already know) there will be a huge added perf tax running locally in dev mode, esp. with devtools open and in particular with rerender highlighting enabled, so please keep that in mind.
You said there was "Not much difference" after upgrading to 3.20.3 - are you able to quantify this? Our measurements showed a pretty significant difference.
If you can provide a concrete repro case of something that is too slow for it to practically work for an editor in production we're happy to look into it.
@bjoerge
Did a re-test with older versions yesterday. Upgrading had no impact. Same impact with latest or year old packages.
Did a test in production now just to make sure I'm not in dev. When I edit the text-input, in the complex example above, I see a 70 ms freeze per keystroke.
I'll see if I can make a repro case later that showcases the issues we are seeing in the project.
I agree with you on the correctness trumps performance part. If debouncing would be introduced then it has to be "invisible" to the user experience.
Here is a psudo example of how I think it could be used (not that it necessary should) In this example the onChange would fire 500 ms after I stop typing AND every 1 second if I type a lot of text really fast. It would ensure only 1 70 ms update every second, and preferably an update not while the user is typing.
... debounce(() => {
triggerOnChange(...);
},
500,
{ maxWait: 1000 }
);
This issue is a bit hard to reproduce consistently. But I think I figured out why.
A fast enough pc doesn't seem to show much lag. If the single core performance is good enough.
When testing, it seems stuff is fine for some pc, but laggy for another. When a pc is slow enough the feeling of lag much more pronounced. I even noticed this one time with my laptop simply because it didn't turbo the CPU that time. That was enough on that laptop to make the lag visible.
You can emulate this by turning on cpu thottling in chrome under performance tab. Stuff should work slower with this on, but typing on my laptop now takes 1 second to update.
Proof of concept for TextInput.tsx that doesn't update on every keystroke.
Here is CPU usage of me typing This is me typing hello world in sanity cms
on current and test build. (The test-build saved 3 times while I wrote)
import {ChangeEventHandler, ChangeEvent, useState, useEffect, FormEvent} from 'react'
import {TextSchemaType} from '@sanity/types'
import {TextArea} from '@sanity/ui'
import styled from 'styled-components'
import {StringInputProps} from '../types'
/**
*
* @hidden
* @beta
*/
export type TextInputProps = StringInputProps<TextSchemaType>
const StyledTextArea = styled(TextArea)`
&[data-as='textarea'] {
resize: vertical;
}
`
function createProxyChangeEvent(value: string): FormEvent<HTMLTextAreaElement> {
const dummyTextArea = document.createElement('textarea')
dummyTextArea.value = value
const customEvent = new Event('change', {
bubbles: true,
}) as unknown as ChangeEvent<HTMLTextAreaElement>
// Set both the target and currentTarget of the event to be the dummy textarea
Object.defineProperty(customEvent, 'target', {
writable: false,
value: dummyTextArea,
})
Object.defineProperty(customEvent, 'currentTarget', {
writable: false,
value: dummyTextArea,
})
return customEvent
}
/**
*
* @hidden
* @beta
*/
export function TextInput(props: TextInputProps) {
const {schemaType, validationError, elementProps} = props
const {value, onChange, ...extraProps} = elementProps
const [inputValue, setInputValue] = useState(value || '')
// Todo: Handle if the value prop updates with something that didn't come from the input itself
const handleChange: ChangeEventHandler<HTMLTextAreaElement> = (e) => {
const newValue = e.currentTarget.value
setInputValue(newValue)
}
useEffect(() => {
const timeout = setTimeout(() => {
onChange(createProxyChangeEvent(inputValue))
}, 500)
return () => {
clearTimeout(timeout)
}
}, [inputValue, onChange])
return (
<StyledTextArea
customValidity={validationError}
value={inputValue}
onChange={handleChange}
placeholder={schemaType.placeholder}
rows={typeof schemaType.rows === 'number' ? schemaType.rows : 10}
{...extraProps}
/>
)
}
@piscopancer If you test with chrome. What does the CPU usage under Performance monitor tools go to?
On my pc the lagging is less obvious if it happens to stay below 100% while I type.
Hey there, running into similar issue, reproducible using the example template-nextjs-personal-website
Once installed test the text input for the timeline components in the project document schema from the structure panel:
As you would expect, the lag is worse in the presentation panel:
I found that nested inputs lag exponentially depending on how deep they are nested. Responsiveness for normal top level string inputs are great though.
Just thought I'd share, I'm a big user of page builders which sometimes have up to four levels of nesting, which unfortunately means a sluggish editor experience.
Judging by https://github.com/sanity-io/sanity/pull/5270 this should be fixed, is it possible something has regressed? When I get a moment ill try installing v3.20.2
FYI this is running on an M1Pro using Sanity 3.23.4, Chrome
EDIT: My issue was testing on a dev server, deployed the text inputs are really fast. Great work thank you!
Text for me is super laggy. It's so laggy it feels distracting and if you typo something you don't notice for a couple seconds until the letters all render. It sort of is the feeling of when you hear yourself echoing on a call but have to keep talking lol.
The video below looks like a slow frame rate but it's actually what I see exactly. I type at least 75-80WPM and you see the letters display in groups but really it should be a smooth letter by letter input and no more than like 1s (how fast I type hello world). I finish typing for nearly 2s before what I typed has been fully displayed.
https://github.com/sanity-io/sanity/assets/20615/04a53d5e-1d69-45c4-8ad3-502354aed312
I'm using Edge 122.0.2365.80 on Mac and Next Sanity 8.0.0 and Sanity Vision 3.29.1
And I'm on a 2.3 GHz 8-Core Intel Core i9 with 32GB of RAM so my computer should be able to handle it fine.
Getting this exact same issue. All fields within Structure are very slow. Are there any updates on this?
As mentioned in my comment, make sure you arent running sanity from a development server
Thanks for the quick response. What would the alternative be if I'm working on a site that's not ready to go live yet? I'm running the studio within a next.js project that gets served when doing npm run dev
. My setup's similar to this: https://www.sanity.io/blog/build-your-own-blog-with-sanity-and-next-js
You could always try npm run build
then npm run start
to use a production server 👍
We have same issue, I have added more information in community slack at this URL https://sanity-io-land.slack.com/archives/C9Z7RC3V1/p1721316378567009
The issue is related to a vast number of API calls- we counted 44 for entering 7 characters.
Does anyone have further updates or solutions to this - we have spent many developer days looking at this and trying everything we can think of, including setting up a new project from the example code base, just to be sure it's not something we are doing.
We have made some improvements by adding a debounce wrapper but have not resolved the underlying issue.
When I type in the text input, there is a 0.5 seconds delay before I see any updates.
What information should I provide you to figure out what's causing the lag? Least I can say is that my app runs on Next 14.