Skip to content

Add core Yoga support for calc()#1874

Open
paradowstack wants to merge 5 commits intofacebook:mainfrom
paradowstack:feat/calc
Open

Add core Yoga support for calc()#1874
paradowstack wants to merge 5 commits intofacebook:mainfrom
paradowstack:feat/calc

Conversation

@paradowstack
Copy link
Copy Markdown

@paradowstack paradowstack commented Feb 12, 2026

Why?

calc() is a core CSS primitive and a common expectation for developers. Adding it to React Native enables more expressive and maintainable styles without JS-side manual calculations. It improves parity across web and native worlds, reducing friction when switching from another platform.

My tweet about this potential feature in React Native was really well received and many people expressed excitement about it.

RN PR link

Summary

This PR intentionally focuses on core engine behaviour and keeps binding/JNI/Java/JS/API completion separate to keep this PR relatively small. I can try to split it even further, if the size will turn out to be too big.

Add core Yoga support for host-owned dynamic style values (including calc()), resolved just-in-time during layout via callback into React Native.

Yoga does not evaluate expressions in its style system. It asks the host to resolve a value using Yoga layout context (node + context + callback id).

Scope

This PR includes core C++ changes for dynamic value storage, callback plumbing, and layout-time resolution.

  • Added YGValueCallback + YGValueCallbackID interface for host-evaluated dynamic values.
  • Extended StyleLength / StyleSizeLength to represent dynamic values and resolve them through callback during layout.
  • Updated style value storage pipeline (StyleValuePool, StyleValueHandle) to store/retrieve callback pointer + id efficiently.

Future Scope

Follow-up PRs will finalise work on this feature. Below is the list of missing scopes:

  • Tests
  • JNI/Java/JS API follow-up as needed

Testing

Tests will be delivered in separate PR.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 12, 2026

@paradowstack is attempting to deploy a commit to the Meta Open Source Team on Vercel.

A member of the Team first needs to authorize it.

@meta-cla meta-cla bot added the CLA Signed label Feb 12, 2026
@paradowstack paradowstack marked this pull request as ready for review February 12, 2026 18:01
@facebook-github-bot facebook-github-bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Feb 12, 2026
@NickGerleman
Copy link
Copy Markdown
Contributor

NickGerleman commented Feb 13, 2026

Still catching up on a lot that happened while I was away, but it’s exciting to see new PRs like this!

When I had looked at to same problem historically, I had a couple intuitions:

Previous CompactFloat for styles didn’t allow representing enough information, why we have the StyleValueHandle and pool here

The styling system should not live in Yoga. As calc() should be supported for more than just layout (eg for animating transforms).

This lets the layout interface, rely on relatively simple point structure, but the consumer must be able to provide context for Yoga to be able to evaluate calc().

My original intention was to add a YGValueCallback style interface, that may be called for just-in-time evaluation of what a style resolves to in the host, given the context Yoga has, like current layout constraints, or the dimensions of the hypothetical containing block.

So YGValueCallback (YGValueDynamic?) could call back to “host”, with the current layout constraints, information about the containing block maybe, for styles which must be dynamically evaluated. That host, with more context, on what a full unit system, screen sizes, inheritance, etc means could then use Yogas context to evaluate something complicated, involving viewport units, percentages of references from Yoga, etc. I started some work here on the React Native side in /react/renderer/css, but I’m not sure if I will be able to come back to it (currently trying to get some RN Text architecture changes our the door, then not sure what).

At one point I had thought this sort of thing would be better interface for Yoga in general. Eg in React Native, we have separate sources of all this style information already, and spend a lot of time trying to creatively maintain separate Yoga and ShadowNode state.

@NickGerleman
Copy link
Copy Markdown
Contributor

NickGerleman commented Feb 13, 2026

In general, I also wanted to move Yogas interface, or at least its internals, closer to “computer style”, where host performs resolution ahead of time. Yoga spends a silly amount of time doing that dynamically, because its most internal interface is before resolution.

@paradowstack
Copy link
Copy Markdown
Author

Thanks so much @NickGerleman for your input!

It totally make sense to separate Yoga from styling system, therefore keep calc() functionality closer to host and evaluate it there.

I have some follow-up questions to be sure we are on the same page:

  1. Should calc() expression coefficients be still be stored in the Yoga layer, or maybe you think it would be better to store them on the React Native side?
  2. Should YGValueCallback be set globally, or maybe set per Node instance?

Thanks in advance!

@NickGerleman
Copy link
Copy Markdown
Contributor

I think it would be easier to store the expression on the RN side. According to CSS spec, we have a phase during evaluation where we simplify as much as possible, then evaluate anything dynamic, but IDK what it looks like in practice.

Re callback, I was originally planning to look at the callback to be per style value, instead of per node. IIRC with StyleValueHandle, we could place a 64 bit pointer in the value pool? Then I'm guessing we would need context from Yoga node in callback as well.

The idea is, for simple cases, RN can do what it is already doing, but when it has a dynamic value that cannot be fully simplified ahead of time, it sets callback/dynamic style, which will hook into RN to evaluate.

@paradowstack paradowstack marked this pull request as draft February 16, 2026 14:55
@paradowstack
Copy link
Copy Markdown
Author

Great - thanks for the answers.

Generally I like this approach. I've already made attempts to implement some parts of it and succeeded.

I don't have any more open questions right now. I will make changes to this PR and, at the same time open a PR to RN repo in order to more clearly see, how these two interacts with each other. I am taking some vacations in the following weeks, so I'm not sure about the timeline, but I will definitely keep you updated! 😊

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
yoga-website Error Error Mar 23, 2026 8:06pm

Request Review

@paradowstack
Copy link
Copy Markdown
Author

Hi @NickGerleman

Vacation period has ended and I pushed the update to this PR.
I have also opened PR to the RN repo, here is the link: facebook/react-native#56162.

This implementation includes just-in-time evaluation by calling back to the host - new YGValueDynamic type.

I am looking forward for your feedback!

@paradowstack paradowstack marked this pull request as ready for review March 19, 2026 19:15
yoga/YGValue.h Outdated
/**
* Callback + identifier pair for internal storage.
*/
struct YGValueDynamicData {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably shouldn't be in YGValue.h

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved this out of this file to StyleLength.h and StyleSizeLength.h

yoga/YGValue.h Outdated
Comment on lines +58 to +61
/**
* Host-defined identifier for a dynamic style value.
*/
typedef uint8_t YGValueDynamicID;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at how this is used in facebook/react-native#56162

"List of all the properties in the world" tends not to scale well.

I wondered, could we use something like void*, then point directly to host native structure?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that using void* would lead to problems when handling cloned properties - that's why I introduced enum as a key - to properly identify and overwrite property with the new value. But I agree that "list of all properties" is not the best solution. That's I have changed implementation and the new solution uses fnv1a hashed value of property name - I think it's cleaner.

Copy link
Copy Markdown
Contributor

@NickGerleman NickGerleman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get some unit tests for this?

Also, sanity checking yoga microbenchmark.

Overall, looks good though!

@paradowstack
Copy link
Copy Markdown
Author

Could we get some unit tests for this?

I have added unit tests.

Also, sanity checking yoga microbenchmark.

image

This is comparison averaged on 10 runs comparing main branch to this branch.
Since the size of StyleLength and StyleSizeLengh increased a bit, and these types are heavily used during layout calculations as a intermediate objects (what can be optimised), there is a difference in layout operation. Please let me know what do you think about these numbers - they definitely can be decreased by further optimizations in Style.h.

@NickGerleman
Copy link
Copy Markdown
Contributor

Any optimization ideas you have for yoga::Style would be great!

Some stuff like this is hard to make 100% pay-for-play, but it starts to really add. E.g. display: contents originally added something like 10%, to end-to-end layout time within Yoga. facebook/react-native@19b0acf

Note that, layout time is more important than tree creation time. I suspect that is slow, because it includes expensive JSON parsing code inside of it (I really ought to see if Claude can fix that...)

@paradowstack
Copy link
Copy Markdown
Author

Any optimization ideas you have for yoga::Style would be great!

Please take a look at this: #1922. Idea behind it is very simple but it does the trick. However I am aware it's not the cleanest optimization - I am happy to hear your thoughts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants