[{"header":"LyteNyte Doc MDX Walkthrough","text":"","link":"blog/md-all","depth":1},{"header":"Official Release of LyteNyte Grid v1.0","text":"A New Era for React Data Grids and Powerful Data Tables","link":"blog/official-release-of-lytenyte-grid-v1","depth":1},{"header":"Introducing LyteNyte Grid","text":"A JavaScript Data Table Library for React Applications","link":"blog/introducing-lytenyte-grid","depth":1},{"header":"Support the 1771 Technologies Way","text":"How We Structured LyteNyte Grid Support","link":"blog/support-the-1771-technologies-way","depth":1},{"header":"Understanding LyteNyte Grid PRO Redistribution","text":"React Data Grid Licensing Decoded","link":"blog/understanding-lytenyte-grid-pro-redistribution","depth":1},{"header":"Who's in Scope with LyteNyte Grid PRO","text":"Determining Developer Coverage","link":"blog/who-is-in-scope-with-lytenyte-grid-pro","depth":1},{"header":"Bringing LyteNyte Grid to Shadcn/UI","text":"From Zero to Styled at Lightspeed","link":"blog/bringing-lytenyte-grid-to-shadcn-ui","depth":1},{"header":"Office Release of LyteNyte Grid v2.0","text":"100% Stateless. The Way Data Grids & Data Tables Should Work","link":"blog/official-release-of-lytenyte-grid-v2","depth":1},{"header":"Headers","text":"Headers Headers create created using the # symbol. Each # is one level of the header. HTML supports 6 levels of headers, however, only the first four are styled by in LyteNyte Doc:","link":"blog/md-all#headers","depth":2},{"header":"Text","text":"Text Normal text is converted into paragraph tags. The standard MD syntax for lists, quotes, images, and tables is also supported. Hello World, BOLD, ITALIC, STRIKETHROUGH Numbered List FirstSecondThird Unordered List Item 1Item 2 List with nested items FirstItem 1Item 2SecondThird Quote here alt TableDescriptionHelloWorldHelloWorldHelloWorldHelloWorld","link":"blog/md-all#text","depth":2},{"header":"Callouts","text":"Callouts :::tip Tip directive. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the ::: :::info Info directive Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the ::: :::warn Warning directive Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the ::: :::danger Danger directive Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the ::: :::best Best directive Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the ::: :::note Note directive Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the :::","link":"blog/md-all#callouts","depth":2},{"header":"Code Blocks","text":"Code Blocks Normal Code block Highlights With Labels Marking Individual Words Word Wrap Line numbers Collapse Parts","link":"blog/md-all#code-blocks","depth":2},{"header":"Math","text":"Math Inline: $$c = \\pm\\sqrta^2 + b^2$$ $$ c = \\pm\\sqrta^2 + b^2 $$","link":"blog/md-all#math","depth":2},{"header":"Better Type Interfaces","text":"Better Type Interfaces Every aspect of the Grid has a typeEvery type has a doc comment explaining","link":"blog/official-release-of-lytenyte-grid-v1#better-type-interfaces","depth":3},{"header":"Game-Changing Headless Component","text":"Game-Changing Headless Component Use our pre-styled themes or drop into full headless mode for 100% controlFully compatible with React's concurrent mode and new compiler If you need a free, open-source data grid (Core) or are shipping enterprise-scale applications that demand advanced features and support (PRO), LyteNyte Grid 1.0 has you covered. One grid, two editions. Both are built for developers who value performance, clarity, and control. If you care about speed, bundle size, flexibility, and an unopinionated API, LyteNyte Grid is the only option. Introducing LyteNyte","link":"blog/official-release-of-lytenyte-grid-v1#game-changing-headless-component","depth":3},{"header":"Better Type Interfaces","text":"Better Type Interfaces Version 1.0 focused specifically on improving the LyteNyte Grid API interface and ensuring we can flexibly add features without causing breaking changes in the future. Stabilized the API interfaceImproved our type docsEnhanced the TypeScript support of both Core and PRO versions of LyteNyte Grid, increasing the compatibility between the two Closer type integration between Core and PRO means users can start with the Core edition of LyteNyte Grid, and if (or when) they need some of the advanced features of PRO, the code changes will be kept to a minimum. No pressure, though, our Core edition is a powerhouse, and for many of the simpler data table use cases, such as sorting, filtering, resizing, and column moving, Core is all you will ever need. We really took the time to improve the API consistency and usability. Our new API is a shallow interface but covers a deep range of features that match and, in many cases, far exceed the capabilities of other data grids. Events can now be prevented, and we've enforced stronger declarative constraints for nearly every part of the grid. It works with React the way React expects it toReworked the internal state management so it's compatible with React's concurrent modeLyteNyte Grid's declarative state is also fully compatible with React's new compiler. No configuration necessary","link":"blog/official-release-of-lytenyte-grid-v1#better-type-interfaces-1","depth":2},{"header":"Halved The Bundle Size","text":"Halved The Bundle Size We cut the total bundle size of LyteNyte Grid in half. With trees shaking enabled, LyteNyte Grid Core edition comes in at just 36kb gzipped (43kb with extras). The PRO edition, with all the advanced features and with tree shaking, clocks in between 49kb and 65kb. For context, these numbers are ridiculously small. Other data grids with a similar feature set weigh 4-7x more. These numbers exceeded our expectations. Our Core edition (free for the community), at just 36kb, still supports row and column virtualization, pagination, row selection, column moving, row dragging, column resizing, master-detail views, full-width rows, column and row spanning, and more. The kicker? Even though LyteNyte Grid 1.0 is already the most feature-rich data grid at the smallest bundle size, we know we can make it smaller. Future versions of LyteNyte Grid will push the bundle size down even further while getting faster at the same time.","link":"blog/official-release-of-lytenyte-grid-v1#halved-the-bundle-size","depth":2},{"header":"Game-Changing Headless Component Interface","text":"Game-Changing Headless Component Interface The Introduction of a new headless component. With LyteNyte Grid 1.0, you have ultimate flexibility to choose between one of our pre-styled themes or drop into full headless mode for 100% control. Covering any use case you may have for a data table library. Most data grids are a black box. A magic component that gets fed all the props and magically renders a view. Want to attach some CSS to a specific cell? Want to add a mouse or keyboard listener? Want to add some magic cell class rules (which you have no control over how or when these rules will be evaluated)? Good luck. The magic box is inflexible - it's less code in the short run, and a nightmare in the long run. LyteNyte Grid throws this paradigm out the window. We've adopted a headless approach. The base grid is completely unstyled and only handles the complex logic required for a data grid. We went from this: To this, Now, it does look like more code under the headless version, but this isn't necessarily true. We've exposed a public interface to interact with any part of the grid without indirection. Want to detect when a cell was clicked? Just add the onClick listener to the Cell component. This pattern is already familiar and natural. Every React developer knows how to do this. Styling is also a breeze. Want to use Tailwind? Go right ahead, no custom styling API to learn. What you already know can be directly applied. We maintain the complexity, and you can create the exact experience your users expect.","link":"blog/official-release-of-lytenyte-grid-v1#game-changing-headless-component-interface","depth":2},{"header":"Next Steps: The Future of LyteNyte Grid","text":"Next Steps: The Future of LyteNyte Grid This is just the start for us. LyteNyte Grid is already the best grid by every metric; it has the most features (most free features too), the smallest bundle size, fastest rendering, and lowest memory footprint. Next for us is to continue to improve our code, since we know there is more performance we can squeeze out, more bytes we can remove, and less memory we can allocate. We have a steady slate of features planned, like rollout headers, declarative prop delegation, dynamic pivots, and 100s more. There is no shortage of features to implement, only a finite amount of time to implement them all. So, are you interested in seeing if we can support a feature you have in mind? Let us know on GitHub, or check out our live demo for more. If you haven't already given LyteNyte Grid a go, it's one NPM install away","link":"blog/official-release-of-lytenyte-grid-v1#next-steps-the-future-of-lytenyte-grid","depth":2},{"header":"Get Started Now","text":"Get Started Now Try the interactive demo. See LyteNyte Grid PRO in action.Compare license plans. Choose the right fit for your team. Or start building with the free LyteNyte Grid Core edition. It's open source, memory efficient, and ready to drop into your next React project.","link":"blog/official-release-of-lytenyte-grid-v1#get-started-now","depth":2},{"header":"Death, Taxes, and Problems with JavaScript Data Tables","text":"Death, Taxes, and Problems with JavaScript Data Tables Before we built LyteNyte Grid, we wasted too much time trying to overcome the limitations of other data grid libraries. Here is what we ran into over and over again: Struggled with fast-updating or real-time datasets.Clunky APIs that made customization extremely tedious.Features like async data loading or exports were not built-in or were only available via plugins and fragile wrappers.Fell apart beyond basic use cases with poor virtualization, excessive re-renders, and mysterious slowdowns.Excessive breaking changes occur with each new version shipped.Lack of comprehensive or advanced features, like server-side data loading. Development teams, including ours, were often forced to patch gaps by duct-taping features, duplicating logic, or forking open-source libraries to fix core problems. These quick fixes usually turned into long-term problems. Technical debt built, developer velocity slowed, and maintenance costs climbed. There had to be a better way!","link":"blog/introducing-lytenyte-grid#death-taxes-and-problems-with-javascript-data-tables","depth":2},{"header":"Hello World! LyteNyte Grid","text":"Hello World! LyteNyte Grid LyteNyte Grid is a data grid built for React applications. It's a high-performance data table designed for developers and enterprises tired of dragging legacy grid libraries into modern codebases, like surfing the modern web over dial-up. LyteNyte Grid comes in two editions: Core Edition: Free, open source (Apache 2.0), and truly useful. Includes essential features such as sorting, filtering, row grouping, column auto-sizing, detail views, data exporting, and others.PRO Edition: A commercial edition (EULA) with advanced capabilities like server data loading, column and filter manager components, tree data, column pivoting, and more sophisticated data table tools. Built by 1771 Technologies, a London-based software company obsessed with developing software that turns your complex data into 'seamless simplicity'.","link":"blog/introducing-lytenyte-grid#hello-world-lytenyte-grid","depth":2},{"header":"Designed for Ludicrous Speed","text":"Designed for Ludicrous Speed LyteNyte Grid is optimized like it's trying out for a racing team. Fast rendering? Check. Smooth scrolling? Naturally. Efficient data handling even with massive, ever-expanding datasets? Absolutely. Every line of code has been carefully refactored to eliminate bottlenecks. Our reactive state architecture means performance doesn't degrade when paginating, filtering, or pulling from a server.","link":"blog/introducing-lytenyte-grid#designed-for-ludicrous-speed","depth":3},{"header":"Clean, Consistent, and Well-Thought-Out API","text":"Clean, Consistent, and Well-Thought-Out API The developer experience matters. LyteNyte Grid works with your React stack, not against it. There is no convoluted setup or over-engineering, just clean React code that plugs into your application's existing state and logic like it belongs there. Because it does.","link":"blog/introducing-lytenyte-grid#clean-consistent-and-well-thought-out-api","depth":3},{"header":"Enterprise Features, Zero Bloat","text":"Enterprise Features, Zero Bloat LyteNyte Grid is packed with features your enterprise genuinely needs, such as pivot tables, tree data, server-side data loading, custom cell rendering, and rich cell editing, all without requiring a dozen dependencies. It's a full-spectrum data grid solution minus the cruft.","link":"blog/introducing-lytenyte-grid#enterprise-features-zero-bloat","depth":3},{"header":"Transparent Licensing and Real Support","text":"Transparent Licensing and Real Support All PRO licenses are perpetual and include 12 months of dedicated technical support and access to updates, upgrades, and new release versions. After 12 months, your PRO license continues to work indefinitely. You can renew your license to continue receiving updates and support. You submit issues via GitHub with your license ID and get a guaranteed 24-hour response from our team. See license plans.","link":"blog/introducing-lytenyte-grid#transparent-licensing-and-real-support","depth":3},{"header":"Built For Developers. Trusted By Organizations","text":"Built For Developers. Trusted By Organizations LyteNyte Grid is ideal for: React developers building dashboards, admin tools, or internal applications without wanting to reinvent the grid (again).Engineering leads who care about maintainable, scalable components that don't crumble under complexity.Organizations that need stable APIs, real support, and a licensing model that doesn't feel like a surprise party. Whether you're an individual developer using the free LyteNyte Grid Core edition or part of an enterprise team rolling out LyteNyte Grid PRO across several applications, LyteNyte Grid scales with you.","link":"blog/introducing-lytenyte-grid#built-for-developers-trusted-by-organizations","depth":2},{"header":"Get Started Now","text":"Get Started Now Try the interactive demo. See LyteNyte Grid PRO in action.Compare license plans. Choose the right fit for your team. Or start building with the free LyteNyte Grid Core edition. It's open source, memory efficient, and ready to drop into your next React project.","link":"blog/introducing-lytenyte-grid#get-started-now","depth":2},{"header":"LyteNyte Grid: Talking Support","text":"LyteNyte Grid: Talking Support With LyteNyte Grid, comparing a dozen support plans is unnecessary. We keep it painfully simple and offer only two commercial license plans, Developer PRO and Organization PRO. Both come with a clear, baked-in support plan, so you can skip figuring out whether you need premium, platinum, or “ultra platinum diamond elite plus” support. All you need to consider is how many developers your license should cover. That's it. If you're on a Developer PRO plan, you get 12 support requests per licensed developer, a 24-hour response time, and a full year of technical support. You also gain access to every update, upgrade, and new release we ship during those 12 months. The Organization PRO plan is like Developer PRO after it hits the gym and takes life more seriously. You still get the 24-hour response time, but now each licensed developer gets 24 standard support requests a year. On top of that, you get six priority requests (16-hour response time) and 2 critical requests with an 8-hour response time. And yes, a dedicated relationship manager who knows your name and possibly your coffee order. No upselling, no unexplained charges buried in fine print. Everything is made clear on the pricing page.","link":"blog/support-the-1771-technologies-way#lytenyte-grid-talking-support","depth":2},{"header":"Why GitHub? Because You're Already There","text":"Why GitHub? Because You're Already There We chose GitHub as our main support channel because it's a platform with which developers are already familiar. You're already there, poking at pull requests and writing meaningful commit messages, so why reinvent the wheel and drag you to an alternative portal? So, when it comes time to submit a question, request an innovative feature, or, sadly, report a bug (we're not angry, just disappointed we didn't catch it first), you're already versed in the art of GitHub Issues. Whether it's filtering, grouping, pivoting, or pagination in a React data grid, you know the procedure. It's easy, fast, and straightforward... like muscle memory, but for complaining in a way that works. However, it's not just about convenience, although we feel like heroes for sparing you another login. Using GitHub keeps us accountable. That means anyone can see how we respond, how quickly we squash bugs, and how we handle your feature requests (even the strange ones). We don't hide existing issues behind a private ticketing system, as if they were classified state secrets; we keep them out in the open, so you don't have to find out what support is like only after you've paid. With us, you can check our GitHub any time and judge for yourself. Naturally, not all issues should be public. For sensitive stuff, like a confidential bug report or a security concern, you can report it privately through the 1771 customer portal or contact us directly. We support private reporting when the situation calls for discretion.","link":"blog/support-the-1771-technologies-way#why-github-because-youre-already-there","depth":2},{"header":"Support Without the Guess Work","text":"Support Without the Guess Work Support isn't just about submitting requests into a black hole. It's about setting expectations so nobody gets an anxiety attack waiting for a reply. That's why every PRO license comes with clearly defined response times. A Developer PRO license plan gives you a 24-hour response time on all requests. Organization PRO does too, but it also lets you escalate issues when things get spicy and comes with a dedicated relationship manager who's in the loop. There's no hidden upgrade needed to get real help. Everything we offer is spelled out in plain daylight. Everything we promise is backed up by how we handle GitHub issues, not a glossy brochure written by a marketing intern on a sugar high. With LyteNyte Grid, it's as vanilla as a JavaScript UI component. If your license needs to cover 1 to 50 developers, go with Developer PRO. If you need to cover more than 50 developers, that's where Organization PRO comes in. Developer PRO teams get solid support. Organization PRO teams get a dedicated contact point and an option to escalate issues. No need to Frankenstein together a custom package. We already did the thinking, so you don't have to. Just pick your lane and go.","link":"blog/support-the-1771-technologies-way#support-without-the-guess-work","depth":2},{"header":"In the End, it's About Transparency and Trust","text":"In the End, it's About Transparency and Trust Support shouldn't feel like feeding tokens at a casino slot machine. You shouldn't have to hope you will get the help you need. Our whole approach is designed to remove that uncertainty. Use GitHub for public issues. Use the customer portal or email for private ones. Everything is visible, auditable, and can be tracked. No smoke, no mirrors, no support roulette. Simple, honest, and developer-first is how we do support at 1771 Technologies.","link":"blog/support-the-1771-technologies-way#in-the-end-its-about-transparency-and-trust","depth":2},{"header":"Get Started Now","text":"Get Started Now Try the interactive demo. See LyteNyte Grid PRO in action.Compare license plans. Choose the right fit for your team. Or start building with the free LyteNyte Grid Core edition. It's open source, memory efficient, and ready to drop into your next React project.","link":"blog/support-the-1771-technologies-way#get-started-now","depth":2},{"header":"Understanding Distribution and Deployment","text":"Understanding Distribution and Deployment LyteNyte Grid PRO license plans grant you a perpetual, royalty-free right to use and install the software across any hosting environment. Whether your application runs on internal servers (on-premises), in the cloud, or is shipped as a SaaS product, the license terms stay the same. There are no added fees tied to your infrastructure or hosting model. What matters is not how or where you host your application; it's who uses it and what rights they are given. If your application is used exclusively within your organization, such as by employees, contractors, or entities under your direct control, it qualifies as internal use. In that case, you have complete flexibility to integrate LyteNyte Grid however you need. If it's made available to users outside your organization, such as customers, clients, or partners, it's considered external use, and additional restrictions on distribution and deployment apply. That distinction is critical to understanding your LyteNyte Grid PRO license.","link":"blog/understanding-lytenyte-grid-pro-redistribution#understanding-distribution-and-deployment","depth":2},{"header":"LyteNyte Grid: Internal Freedom and External Responsibility","text":"LyteNyte Grid: Internal Freedom and External Responsibility Once you understand the difference between internal and external use, applying our licensing model becomes straightforward. Internal use provides complete flexibility. You're free to distribute, deploy, integrate, and customize LyteNyte Grid however you need, whether in dashboards, admin panels, internal libraries, reusable components, or internal development toolkits. There are no functional restrictions, as long as you stay within your licensed developer count. External use is permitted, but with clearly defined boundaries. You can distribute or deploy applications externally, but only under specific conditions designed to preserve the integrity of the licensing model. External use requires LyteNyte Grid to be part of an integrated application and not be exposed for reuse, sublicensing, or developer-facing functionality by third parties. An integrated application is a complete, functional application that includes LyteNyte Grid as a component of a larger feature set and is intended for end use by your customers. In a nutshell, internal use means full control; for external use, different rules apply.","link":"blog/understanding-lytenyte-grid-pro-redistribution#lytenyte-grid-internal-freedom-and-external-responsibility","depth":2},{"header":"What External Use Can't Include","text":"What External Use Can't Include When you distribute or deploy externally, the guiding principle is simple: LyteNyte Grid must always be part of a complete, integrated application used by your end customers, not something they use to build with. If your application turns the grid into a development tool, a reusable resource, or a launching point for someone else's software, that is outside the license scope.","link":"blog/understanding-lytenyte-grid-pro-redistribution#what-external-use-cant-include","depth":2},{"header":"The 7 Restrictions for External Use","text":"The 7 Restrictions for External Use Here's what you cannot do under a LyteNyte Grid PRO license: Don't offer it as a standalone product. LyteNyte Grid can't be sold, distributed, or deployed as a standalone product. It must always be embedded within a larger, fully integrated application.Don't expose it for reuse. You may not ship LyteNyte Grid or its parts as a shared module, component, wrapper, package, or any way that allows it to be reused across projects or accessed by other applications, libraries, or developer environments.Don't build something that competes with it. You can't create derivative products, components, libraries, or wrappers that compete with or substantially replicate the functionality of LyteNyte Grid PRO.Don't use it in developer tools or to create them. That includes toolkits, application or website builders, low-code platforms, drag-and-drop editors, admin generators, or any application intended for use by software, application, or website developers or designers.Don't share the source code. You can't distribute or deploy LyteNyte Grid in source code form or make it available in any form that allows others to modify, extend, or reverse-engineer it.Don't open-source it. LyteNyte Grid can't be incorporated into any project with licenses requiring source disclosure, allowing derivative works, or permitting free redistribution.Don't sublicense it. Your license doesn't transfer to your customers, partners, or downstream users. They must obtain their own license if they need development access. These restrictions apply no matter how your software is delivered. SaaS, on-prem, downloadable packages, and hosted services are all subject to the same rules. Packaging, bundling, or obfuscation does not reduce or eliminate your licensing obligations. Build for users, not for builders. That single distinction defines the boundary. Understanding redistribution chart Read the EULA. For the full licensing terms.","link":"blog/understanding-lytenyte-grid-pro-redistribution#the-7-restrictions-for-external-use","depth":3},{"header":"Your Responsibility as the License Holder","text":"Your Responsibility as the License Holder Everything covered, from integrated applications to external use, rests on a straightforward principle: your organization holds the license rights. When you license LyteNyte Grid PRO, those rights apply solely to your organization and licensed developers. They are non-transferable and cannot be sublicensed or extended to customers, clients, or downstream users. If you distribute or deploy an external application that includes LyteNyte Grid, you're responsible for ensuring it can't be extracted, reused, or turned into a development tool by end users. That obligation doesn't shift downstream. It stays with you. Here's what that looks like in practice: End users must not be able to access or modify LyteNyte Grid as a module, configurable component, or anything else that can be built on.You cannot grant downstream users any rights to the grid, regardless of how the software is bundled or compiled.Your product should include terms of use that reflect the license limitations, specifically prohibiting reuse, redistribution, sublicensing, and development access.If your end users want to build with LyteNyte Grid, they must obtain their own license from 1771 Technologies. Licensing LyteNyte Grid is simple. Staying compliant is too, and it works because you make it work.","link":"blog/understanding-lytenyte-grid-pro-redistribution#your-responsibility-as-the-license-holder","depth":2},{"header":"Clear Terms. Confident Shipping","text":"Clear Terms. Confident Shipping You don't need to decipher overly complicated license terms. If you're building for internal users, LyteNyte Grid gives you full freedom. If you're deploying externally, just keep it part of a complete, integrated application and not a tool others can build with. That's it. We designed the LyteNyte Grid PRO license to eliminate the guesswork that frustrates many teams. No vague terms. No per-deployment fees. No convoluted exceptions buried in legalese. You get clarity, flexibility, and peace of mind, whether building dashboards for your operations team or deploying a production-grade SaaS platform. If you're tired of decoding license plans and just want to focus on shipping software, LyteNyte Grid PRO is made for you.","link":"blog/understanding-lytenyte-grid-pro-redistribution#clear-terms-confident-shipping","depth":2},{"header":"Get Started Now","text":"Get Started Now Try the interactive demo. See LyteNyte Grid PRO in action.Compare license plans. Choose the right fit for your team. Or start building with the free LyteNyte Grid Core edition. It's open source, memory efficient, and ready to drop into your next React project.","link":"blog/understanding-lytenyte-grid-pro-redistribution#get-started-now","depth":2},{"header":"Determining Who Needs a LyteNyte Grid License","text":"Determining Who Needs a LyteNyte Grid License In a nutshell, the number of developers covered by your license must equal the number of concurrent developers who write, modify, or contribute to the front-end codebase of any project that uses or integrates LyteNyte Grid or its derivative works. At any given time, any developer actively contributing to the front-end code must be licensed, whether interacting directly with LyteNyte Grid, accessing it indirectly through intermediate layers or wrappers, or focusing on other parts of the project's front-end code. The license requirement is based on the number of developers concurrently active in the front-end code, not the total number who have ever contributed. Developers who perform both front-end and back-end coding tasks must also be licensed. A license is not required for roles that do not write, modify, or contribute changes to the front-end codebase, such as UX/UI designers who work exclusively in design platforms like Figma. To make developer licensing even clearer, we will break it down piece by piece, like deconstructing a LEGO set, with a few examples to clarify everything.","link":"blog/who-is-in-scope-with-lytenyte-grid-pro#determining-who-needs-a-lytenyte-grid-license","depth":2},{"header":"Illustration One: Merlin Securities LLC","text":"Illustration One: Merlin Securities LLC Merlin Securities LLC is developing AppX, a portfolio dashboard for stock data. AppX will include financial charts, custom views, and a lightning-fast React data grid powered by LyteNyte Grid. Merlin Securities has assigned 9 employees to work on AppX. Of these, 4 developers actively contribute to the front-end codebase, where LyteNyte Grid is integrated. This group includes developers working directly with LyteNyte Grid, those contributing to other areas of the front-end code, and a full-stack developer who splits time between front-end and back-end tasks. Under LyteNyte Grid's licensing model, all 4 developers must be licensed because they are writing, modifying, or contributing to the front-end code of a project that uses LyteNyte Grid, regardless of whether they touch LyteNyte Grid directly or work elsewhere in the front-end codebase. The remaining five employees are back-end developers, a UX/UI designer, and a QA tester. Since their work does not involve writing or modifying the front-end codebase, they do not require a LyteNyte Grid license. :::note QA testers typically do not require a LyteNyte Grid license unless their role involves contributing code to the front-end. ::: Illustration one: merlin securities","link":"blog/who-is-in-scope-with-lytenyte-grid-pro#illustration-one-merlin-securities-llc","depth":2},{"header":"Illustration Two: Wayne Industrial Inc.","text":"Illustration Two: Wayne Industrial Inc. Wayne Industrial Inc. is developing Bats-UI, an internal UI component library that integrates LyteNyte Grid and other proprietary components. The Bats-UI team consists of 3 developers contributing to its front-end code, while a UX/UI designer assists the project without writing code. Bats-UI is integrated into AppZ, a cybersecurity dashboard maintained by a different engineering team of 5 developers. Of those, 2 developers contribute to the AppZ's front-end code, which uses Bats-UI and therefore LyteNyte Grid, while the remaining 3 developers work exclusively on the back-end code. Under LyteNyte Grid's licensing model, developers who contribute to the front-end code of any project that uses LyteNyte Grid, whether directly or via a wrapper like Bats-UI, must be licensed. As a result, Wayne Industrial is required to license 5 developers: 3 working on the Bats-UI front-end.2 working on the AppZ front-end. Team members who do not contribute to the front-end code, such as back-end developers and designers, do not require a license. Illustration one: wayne industrial","link":"blog/who-is-in-scope-with-lytenyte-grid-pro#illustration-two-wayne-industrial-inc","depth":2},{"header":"Illustration Three: Oscorp EdTech Inc.","text":"Illustration Three: Oscorp EdTech Inc. Oscorp EdTech Inc. is developing NxO Grid, an internal derivative of LyteNyte Grid with customized features. A team of 2 developers contributes to its front-end code and interacts directly with LyteNyte Grid. NxO Grid is integrated into Goblin-UI, an internal UI component library maintained by a separate team of 2 developers. These developers directly contribute to Goblin-UI's front-end code, indirectly using LyteNyte Grid through its derivative, NxO Grid. Additionally, 2 UX/UI designers support both projects without writing code. Goblin-UI is integrated into App-Spectra, a learning analytics dashboard built by another team of 4 developers. Of these, 2 developers contribute to App-Spectra's front-end code, indirectly using LyteNyte Grid via Goblin-UI and NxO Grid. The remaining 2 developers work exclusively on the back-end code without interaction with the front-end codebase. Under LyteNyte Grid's licensing model, any developer contributing to the front-end code of a project using LyteNyte Grid or its derivatives must be licensed, even if the grid is accessed indirectly through internal libraries or wrappers. Therefore, Oscorp EdTech Inc. must license 6 developers: 2 working on the NxO Grid front-end.2 working on the Goblin-UI front-end.2 working on the App-Spectra front-end. Back-end-only developers and non-coding contributors like UX/UI designers do not require a license. Illustration one: Oscorp EdTech","link":"blog/who-is-in-scope-with-lytenyte-grid-pro#illustration-three-oscorp-edtech-inc","depth":2},{"header":"Illustration 4: Stark Enterprises Ltd.","text":"Illustration 4: Stark Enterprises Ltd. Stark Logistics Ltd. is developing 3 internal applications to modernize its business operations. App-Infinity: A dashboard to view all business transactions and customer payments.App-Arc: An analytics platform that displays real-time shipment records and global delivery logs.App-M3: A routing dashboard designed to assist couriers during last-mile delivery. LyteNyte Grid will be integrated into App-Infinity and App-Arc. App-M3 does not integrate LyteNyte Grid and is being developed in-house with the team's proprietary tools. Each application is being developed by a separate engineering team assigned by Stark Logistics. Each team consists of 2 developers contributing to the front-end code and 2 working solely on the back-end. There are no restrictions on how many applications you can build with LyteNyte Grid PRO. Under the LyteNyte Grid licensing model, a company only needs to determine the number of developers contributing to the front-end code of applications that use LyteNyte Grid or its derivatives. That's all there is to it. Since App-M3 doesn't use LyteNyte Grid, directly or indirectly, none of its developers require a license. However, because App-Infinity and App Arc integrate LyteNyte Grid, all developers contributing to those applications' front-end code must be licensed. As a result, Stark Logistics must license 4 developers: 2 working on App-Infinity front-end.2 working on App-Arc front-end. Illustration one: Stark Enterprises","link":"blog/who-is-in-scope-with-lytenyte-grid-pro#illustration-4-stark-enterprises-ltd","depth":2},{"header":"Concurrency & License Transferability","text":"Concurrency & License Transferability In real-world engineering teams, developers frequently switch between projects. People leave, join, or shift roles, and a LyteNyte Grid PRO plan is built to accommodate that. Our licenses are not associated with specific developers and can be reassigned internally as your team changes over time. The key requirement is that at any given time, the number of developers contributing to the front-end code of a project that includes LyteNyte Grid doesn't exceed the number of licensed seats on your plan. This is what we mean by 'concurrent developers'. You stay compliant as long as the number of front-end contributors working on the project simultaneously does not exceed your seat count. Internal reassignments are allowed because the model is built for flexibility, as long as you remain within your licensed total. If you need to increase your licensed developer count, you can do so at any time through the client portal. Licenses cannot be transferred outside your organization. Reassignment is permitted only within the same legal entity (or one you directly control). This ensures the license is used only by your organization, on your team's projects, as originally intended.","link":"blog/who-is-in-scope-with-lytenyte-grid-pro#concurrency--license-transferability","depth":2},{"header":"License Clarity, Scope Confirmed","text":"License Clarity, Scope Confirmed Not knowing how to determine the number of developers your license needs to cover isn't just a legal risk; it's lost time, internal confusion, and hesitation to ship. After walking through the examples, you have a clear and actionable roadmap for identifying who's in scope and how to stay compliant. But the benefit goes beyond compliance. Knowing precisely who needs to be licensed gives your team the confidence to adapt, shift, and scale without second-guessing. A LyteNyte Grid PRO plan removes the friction of uncertainty, giving your organization a predictable, future-proof runway as your team evolves.","link":"blog/who-is-in-scope-with-lytenyte-grid-pro#license-clarity-scope-confirmed","depth":2},{"header":"Get Started Now","text":"Get Started Now Try the interactive demo. See LyteNyte Grid PRO in action.Compare license plans. Choose the right fit for your team. Or start building with the free LyteNyte Grid Core edition. It's open source, memory efficient, and ready to drop into your next React project.","link":"blog/who-is-in-scope-with-lytenyte-grid-pro#get-started-now","depth":2},{"header":"Setup LyteNyte + Shadcn/ui in 60s","text":"Setup LyteNyte + Shadcn/ui in 60s LyteNyte Grid is a powerful React grid library built for massive scale, full Tailwind compatibility, and maximum flexibility. It's the only grid that can be headless for full control or pre-styled with one of our sleek themes. We've now added native themes for shadcn/ui (dark and light), using shadcn's own Tailwind token system. For developers, that means: No extra styling layers to manage.If you update your theme tokens, the grid updates automatically.It looks and feels like a natural extension of your shadcn/ui application. You can install it using the standard shadcn/ui command and get up and running in seconds. Check out our Installation with Shadcn guide for more details, or simply run:","link":"blog/bringing-lytenyte-grid-to-shadcn-ui#setup-lytenyte--shadcnui-in-60s","depth":2},{"header":"Bringing Shadcn/ui to All LyteNyte Grid Users","text":"Bringing Shadcn/ui to All LyteNyte Grid Users The new Shadcn themes are part of our open-source LyteNyte Grid Core edition, which, at only 36kb (gzipped), already offers powerful features for free, such as: Row groupingMaster-detail rowsData aggregation So, if you're building dashboards, admin panels, or internal tools and want them to feel native to shadcn/ui, LyteNyte Grid handles the heavy lifting so you can focus on features, not plumbing.","link":"blog/bringing-lytenyte-grid-to-shadcn-ui#bringing-shadcnui-to-all-lytenyte-grid-users","depth":2},{"header":"Get Started Now","text":"Get Started Now Try the interactive demo. See LyteNyte Grid PRO in action.Compare license plans. Choose the right fit for your team. Or start building with the free LyteNyte Grid Core edition. It's open source, memory efficient, and ready to drop into your next React project.","link":"blog/bringing-lytenyte-grid-to-shadcn-ui#get-started-now","depth":2},{"header":"What's New in LyteNyte Grid 2.0?","text":"What's New in LyteNyte Grid 2.0? Great developer experience means the data grid gets out of your damn way. So, LyteNyte Grid 2.0 has gone 100% stateless and fully prop-driven. Meaning you can configure it declaratively from your state, whether it's URL params, server state, Redux, or whatever else you can imagine. Statelessness is not the only improvement. LyteNyte Grid 2.0 is stacked with improvements:","link":"blog/official-release-of-lytenyte-grid-v2#whats-new-in-lytenyte-grid-20","depth":2},{"header":"Limitless API Extensions","text":"Limitless API Extensions Extend API and column definitions with your own properties and methods100% type-safe, naturally","link":"blog/official-release-of-lytenyte-grid-v2#limitless-api-extensions","depth":3},{"header":"Featherweight Bundle Size","text":"Featherweight Bundle Size PRO: 40kb (down 18% from 49kb)Core: 30kb (down 14% from 35kb)","link":"blog/official-release-of-lytenyte-grid-v2#featherweight-bundle-size","depth":3},{"header":"Heavy-Hitting New Features","text":"Heavy-Hitting New Features Post-group filters, pivot grand totals, resizable column groupsRow tree collapsing and advanced label filtering","link":"blog/official-release-of-lytenyte-grid-v2#heavy-hitting-new-features","depth":3},{"header":"Improved Documentation","text":"Improved Documentation +130 guides that cover real-world scenariosLive demos, detailed code explanations If you need a free, open-source data grid (Core) or are shipping enterprise-scale applications that demand advanced features and support (PRO), LyteNyte Grid 2.0 has you covered.","link":"blog/official-release-of-lytenyte-grid-v2#improved-documentation","depth":3},{"header":"Limitless API Extensions","text":"Limitless API Extensions LyteNyte Grid 2.0 now supports virtually unlimited extensibility because we acknowledge that only you understand your business logic best. You can now extend the Grid API and column definitions with your own custom properties and methods, all fully type-safe. Instead of making you rebuild your app around our ego, we designed the grid to fit your architecture.","link":"blog/official-release-of-lytenyte-grid-v2#limitless-api-extensions-1","depth":2},{"header":"Hybrid Headless Mode","text":"Hybrid Headless Mode LyteNyte Grid 1.0 is fully headless. LyteNyte Grid 2.0 keeps that power and remains headless, but now provides a sensible default child configuration. This lets you get up and running quickly without writing the 'Mount Everest' of boilerplate. Developers can now simply write: Instead of:","link":"blog/official-release-of-lytenyte-grid-v2#hybrid-headless-mode-1","depth":2},{"header":"Native Tree Data","text":"Native Tree Data Tree Data got completely reworked in LyteNyte Grid 2.0. Instead of forcing array-based path structures, the grid now works directly with object-based data. We ditched “Tree Data Mode”. Most tree-data needs were already handled by the standard client grid, yet most grids force you to flatten nested objects into array paths just to render a hierarchy. We don't. LyteNyte Grid works directly with your nested object data, so it can render and edit deep hierarchies natively.","link":"blog/official-release-of-lytenyte-grid-v2#native-tree-data-1","depth":2},{"header":"Brand New Components","text":"Brand New Components LyteNyte Grid 2.0 ships with a high-performance component suite you can run with the grid or use independently: Smart Select: Component for combobox or multi-chip selection.Tree View: Built as a grid variant for specialized hierarchies.Menus & Dialogs: Fully functional context menus and a general-purpose dialog component. Since we dislike repetitive setup as much as you do, we've also shipped utility components and helpers that minimize grid-related boilerplate.","link":"blog/official-release-of-lytenyte-grid-v2#brand-new-components-1","depth":2},{"header":"Featherweight Bundle Size","text":"Featherweight Bundle Size We tightened the codebase to make LyteNyte Grid 2.0 significantly faster and lighter, even with the new features. Core (Free Edition): 30kb gzipped (Down from 35kb).Pro (Commercial Edition): Under 40kb gzipped (Down from 49kb). Less code. More power.","link":"blog/official-release-of-lytenyte-grid-v2#featherweight-bundle-size-1","depth":2},{"header":"Heavy Hitting New Features","text":"Heavy Hitting New Features DX wasn't the only upgrade. We loaded 2.0 with a wide range of new features: Post-group filtersRow group tree collapsingResizable column groupsLabel filteringGrand total rows for pivot mode And a hell of a lot more.","link":"blog/official-release-of-lytenyte-grid-v2#heavy-hitting-new-features-1","depth":2},{"header":"Improved Documentation","text":"Improved Documentation We know developers don't want to read documentation, mostly because docs are typically awful. That's why we fixed ours. We didn't merely wish you luck and automatically generate an API reference. We wrote 130+ in-depth guides, each with thorough explanations, real-world demos, and code examples. Everything you would need to get productive with LyteNyte Grid fast.","link":"blog/official-release-of-lytenyte-grid-v2#improved-documentation-1","depth":2},{"header":"Next Steps: Beyond The Opening Act","text":"Next Steps: Beyond The Opening Act This is only the beginning for us. LyteNyte Grid 2.0 has been significantly shaped by feedback from existing users, and we're grateful for it. We have a steady slate of features planned, including rollout headers, advanced expression filters, and more. If there is a feature you have in mind? Let us know on GitHub, or check out our live demo for more. Give LyteNyte Grid a go, it's one NPM install away","link":"blog/official-release-of-lytenyte-grid-v2#next-steps-beyond-the-opening-act","depth":2},{"header":"Get Started Now","text":"Get Started Now Try the interactive demo. See LyteNyte Grid PRO in action.Compare license plans. Choose the right fit for your team. Or start building with the free LyteNyte Grid Core edition. It's open source, memory efficient, and ready to drop into your next React project.","link":"blog/official-release-of-lytenyte-grid-v2#get-started-now","depth":2},{"header":"Bulk Cell Editing","text":"Use the LyteNyte Grid API to perform bulk cell updates. Provide updated data for targeted rows, and the grid will update the corresponding cells as if they were edited directly.","link":"docs/cell-editing-bulk-editing","depth":1},{"header":"Full Row Cell Editing","text":"Edit some or all cells in a row at once. When cells are edited as a row, the grid applies and commits these changes as a single row edit.","link":"docs/cell-editing-full-row","depth":1},{"header":"Linked Cell Edits","text":"LyteNyte Grid handles edits on a cell-by-cell basis but applies changes to the entire row. This allows you to link cells so that updating one value automatically updates others within the same row.","link":"docs/cell-editing-linked-cell-edits","depth":1},{"header":"Cell Edit Renderers","text":"To enable cell editing, provide an edit renderer for each editable column. This guide covers edit renderers for basic data types.","link":"docs/cell-editing-renderers","depth":1},{"header":"Cell Edit Validation","text":"Validation is essential to maintain data integrity when cell editing is enabled in LyteNyte Grid. Verify cell values and display clear error messages for invalid data.","link":"docs/cell-editing-validation","depth":1},{"header":"Cell Editing","text":"Configure LyteNyte Grid to modify and update data directly with single-cell editing.","link":"docs/cell-editing","depth":1},{"header":"Cell Diff Flashing","text":"Highlight frequent data updates by momentarily flashing modified cells. This visual cue helps users track real-time changes in high-frequency data streams.","link":"docs/cell-diff-flashing","depth":1},{"header":"Cell Renderers","text":"Cell renderers are custom React components you assign to a column definition to control what each cell displays. Assign one to a specific column or set it in the base column definition to apply it to every grid cell.","link":"docs/cell-renderers","depth":1},{"header":"Cell Range Selection","text":"LyteNyte Grid supports selecting single or multiple ranges. A range is a rectangular block of cells that the grid marks as selected.","link":"docs/cell-selection","depth":1},{"header":"Cell Tooltips & Popovers","text":"Use custom cell renderers to add tooltips or popovers to cells. LyteNyte Grid is unopinionated, allowing direct integration with your existing components.","link":"docs/cell-tooltips","depth":1},{"header":"Client Row Aggregations","text":"LyteNyte Grid's client data source allows you to aggregate grouped-row data to produce values for display at the group level.","link":"docs/client-source-aggregations","depth":1},{"header":"Client Row Data","text":"Client row data is an array of rows fully loaded in the browser. This guide outlines the key considerations for working with client row data.","link":"docs/client-source-data","depth":1},{"header":"Client Row Filtering","text":"LyteNyte Grid's client row source lets you to set custom filters to exclude specific rows from view. These filtered rows are omitted before actions such as grouping, sorting, or aggregating are executed.","link":"docs/client-source-filtering","depth":1},{"header":"Client Row Having Filters","text":"Having filters exclude group rows and their children after the grid performs grouping. Apply these filters to all groups or target specific depths.","link":"docs/client-source-having-filters","depth":1},{"header":"Client Row Label Filters","text":"Learn how to use row label filters to display only row groups with valid labels.","link":"docs/client-source-label-filters","depth":1},{"header":"Client Row Overview","text":"Learn how to handle client-side data to deliver a seamless user experience. LyteNyte Grid allows you to display, edit, sort, group, and filter row data directly in the browser.","link":"docs/client-source-overview","depth":1},{"header":"Client Row Adding","text":"Learn how to add rows to an existing dataset using LyteNyte Grid's client data source. Insert rows at any position.","link":"docs/client-source-row-adding","depth":1},{"header":"Client Row Deleting","text":"Delete rows using LyteNyte Grid's row data source and ensure the deletion updates your client data.","link":"docs/client-source-row-deleting","depth":1},{"header":"Client Row Group Collapsing","text":"Row grouping transforms row data into a tree structure. LyteNyte Grid flattens this tree if a row group contains only a single leaf child.","link":"docs/client-source-row-group-collapsing","depth":1},{"header":"Client Row Grouping","text":"Group rows by one or more dimensions to create a hierarchical representation of your data. Row groups can be uniform, with equal dimensions, or non-uniform, with varying dimensions.","link":"docs/client-source-row-grouping","depth":1},{"header":"Client Row Sorting","text":"Sort client row data in either ascending or descending order and apply multiple sorts, where each successive sort resolves equal comparisons from the previous sorts.","link":"docs/client-source-row-sorting","depth":1},{"header":"Client Row Pinning","text":"Configure the client row source to pin rows to the top or bottom of the grid viewport. Pinned rows will remain visible as the user scrolls.","link":"docs/client-source-row-pinning","depth":1},{"header":"Column Autosizing","text":"LyteNyte Grid can automatically size columns to fit their content. Columns do not need to be visible to be autosized. The grid uses heuristic functions to determine optimal widths based on content.","link":"docs/column-autosizing","depth":1},{"header":"Column Base","text":"LyteNyte Grid applies a set of default options to the columns you provide. You can override these defaults through the column base object.","link":"docs/column-base","depth":1},{"header":"Column Field","text":"A column's field determines how LyteNyte Grid retrieves a cell value. This guide explains the four field types supported by LyteNyte Grid.","link":"docs/column-field","depth":1},{"header":"Column Floating Header","text":"LyteNyte Grid's floating row is a specialized header row that appears below the main column headers.","link":"docs/column-floating-header","depth":1},{"header":"Column Groups","text":"LyteNyte Grid lets you organize columns into groups to create visual relationships between related columns. Each column belongs to one group, and groups may contain nested groups to form hierarchies.","link":"docs/column-groups","depth":1},{"header":"Column Header Height","text":"LyteNyte Grid lets you customize header height flexibly. The header's total height comes from the combined height of column group headers, column headers, and floating headers.","link":"docs/column-header-height","depth":1},{"header":"Column ID & Name","text":"Every column in LyteNyte Grid must have a unique ID. LyteNyte Grid uses this ID to manage column related state. When the ID is not human readable, you can provide a display name.","link":"docs/column-header-name","depth":1},{"header":"Column Header Renderer","text":"Every column may define a unique header renderer to determine the content that should be displayed in the header cell.","link":"docs/column-header-renderer","depth":1},{"header":"Column Moving","text":"Columns in LyteNyte Grid may be reordered programmatically or through the grid's drag-and-drop header interactions. Column groups can also be reordered as a unit.","link":"docs/column-moving","depth":1},{"header":"Column Pinning","text":"LyteNyte Grid supports pinning columns to the start or end of the viewport. Pinned columns stay visible as the user scrolls horizontally.","link":"docs/column-pinning","depth":1},{"header":"Column Resizing","text":"Columns can be resized programmatically or through user interaction. LyteNyte Grid includes built-in drag-to-resize behavior for column headers.","link":"docs/column-resizing","depth":1},{"header":"Column Spanning","text":"With LyteNyte Grid, cells can span multiple columns. When a cell spans, it extends into adjacent columns, and the grid skips rendering any cells it covers.","link":"docs/column-spanning","depth":1},{"header":"Column Visibility","text":"Learn how to manage column visibility in LyteNyte Grid and understand the difference between hidden columns and collapsed column groups.","link":"docs/column-visibility","depth":1},{"header":"Columns Overview","text":"LyteNyte Grid uses columns to define the data displayed in the grid's viewport. This guide details how to use columns to configure dynamic views.","link":"docs/columns","depth":1},{"header":"Marker Column","text":"The marker column is an auxiliary column created and managed by LyteNyte Grid. It is always pinned to the start of the viewport.","link":"docs/marker-column","depth":1},{"header":"Column Manager","text":"Use an opinionated Column Manager component to handle column visibility and ordering.","link":"docs/component-column-manager","depth":1},{"header":"Context Menu","text":"LyteNyte Grid's context menu appears on right-click and provides auxiliary actions for the clicked element.","link":"docs/component-context-menu","depth":1},{"header":"Dialog","text":"Use a dialog to present important information, prompt decisions, or collect input in a modal overlay.","link":"docs/component-dialog","depth":1},{"header":"Grid Overlays","text":"LyteNyte Grid can display overlay content over the viewport or over the rows.","link":"docs/component-grid-overlays","depth":1},{"header":"Menu Button","text":"Use the Menu component to display a dropdown list of user actions.","link":"docs/component-menu-button","depth":1},{"header":"Components Overview","text":"LyteNyte Grid includes a collection of components designed for the grid that also function as standalone components.","link":"docs/component-overview","depth":1},{"header":"Pill Manager","text":"Use the Pill Manager to manage data items, selection, order, and state using a list of pills with drag-and-drop support.","link":"docs/component-pill-manager","depth":1},{"header":"Popover","text":"LyteNyte Grid exports a general-purpose Popover component. You can use it in the grid or elsewhere in your application.","link":"docs/component-popover","depth":1},{"header":"Row Group Cell","text":"Use LyteNyte Grid's Row Group Cell component to easily expand and collapse row groups.","link":"docs/component-row-group-cell","depth":1},{"header":"Select All","text":"Use LyteNyte Grid's checkbox component to select all rows and render checked and indeterminate states.","link":"docs/component-select-all","depth":1},{"header":"Smart Select","text":"LyteNyte Grid's Smart Select component is a combobox that supports single or multiple selection and asynchronous loading.","link":"docs/component-smart-select","depth":1},{"header":"Tree View","text":"Use the Tree View component to render hierarchical data in an expandable and collapsible list.","link":"docs/component-tree-view","depth":1},{"header":"Viewport Shadows","text":"Use the viewport shadows slot to render shadows for pinned sections and add visual depth.","link":"docs/component-viewport-shadows","depth":1},{"header":"Export Arrow","text":"Export grid data to Apache Arrow using LyteNyte Grid's export API.","link":"docs/export-arrow","depth":1},{"header":"Clipboard","text":"Use the grid's cell selection and export API to copy grid data to the clipboard.","link":"docs/export-clipboard","depth":1},{"header":"CSV Data Export","text":"Export grid data to CSV using LyteNyte Grid's export API.","link":"docs/export-csv","depth":1},{"header":"Export Excel","text":"Export grid data to Excel using third-party libraries like ExcelJS.","link":"docs/export-excel","depth":1},{"header":"Export Overview","text":"LyteNyte Grid exports structured grid data to create files in various formats or to copy to the clipboard.","link":"docs/export-overview","depth":1},{"header":"Export Parquet","text":"Export grid data to Apache Parquet using LyteNyte Grid's export API.","link":"docs/export-parquet","depth":1},{"header":"Filtering Best Practices","text":"Follow these guidelines for a better filtering experience. This section lists recommended practices.","link":"docs/filtering-best-practices","depth":1},{"header":"Filtering Dates","text":"Create custom date filters in LyteNyte Grid. This guide covers common filters for date cell values.","link":"docs/filtering-dates","depth":1},{"header":"Filtering Numbers","text":"Create custom number filters in LyteNyte Grid. This guide covers common filters for numeric cell values.","link":"docs/filtering-numbers","depth":1},{"header":"Quick Search Filtering","text":"A quick search filter uses a query string to filter rows by matching the search term against all or selected values in a row.","link":"docs/filtering-quick-search","depth":1},{"header":"Set Filtering","text":"Create a filter that includes or excludes rows based on whether a cell value appears in a set.","link":"docs/filtering-set-filters","depth":1},{"header":"API Extensions","text":"Extend the LyteNyte Grid API with additional properties for each grid instance. Use these properties to share custom functionality across renderers provided to the grid.","link":"docs/grid-api-extensions","depth":1},{"header":"Filtering Text","text":"Create custom text filters in LyteNyte Grid. This guide covers common filters for string cell values.","link":"docs/filtering-text","depth":1},{"header":"Responsive Container","text":"Learn how to create a responsive container for LyteNyte Grid using various approaches, from fixed sized containers to grid and flex based containers.","link":"docs/grid-container","depth":1},{"header":"Headless Component Parts","text":"LyteNyte Grid is a headless data grid. Each part of the grid is split into constituent components that you can compose declaratively to form the grid view.","link":"docs/grid-headless-parts","depth":1},{"header":"React Compiler","text":"LyteNyte Grid, built in React for React, requires no special configuration to start using the React compiler. Simply set up the compiler as part of your build process, and it will work seamlessly.","link":"docs/grid-react-compiler","depth":1},{"header":"Grid Reactivity","text":"LyteNyte Grid is a declarative grid. The state you apply determines what the grid displays. The design follows the philosophy that \"view is a function of state.\"","link":"docs/grid-reactivity","depth":1},{"header":"Row & Column Virtualization","text":"LyteNyte Grid virtualizes rows and columns. This guide explains virtualization, highlights its performance benefits, and outlines key considerations for developers.","link":"docs/grid-virtualization","depth":1},{"header":"Infinite Row Filtering","text":"Infinitely load filtered rows on demand from the server.","link":"docs/infinite-rs-row-filtering","depth":1},{"header":"Infinite Row Sorting","text":"Provide a sort model to the server data source to infinitely load sorted rows as the user scrolls.","link":"docs/infinite-rs-row-sorting","depth":1},{"header":"Infinite Rows","text":"Use the server data source to implement infinite scrolling and fetch rows as the user scrolls toward the bottom of the grid viewport.","link":"docs/infinite-rs-rows","depth":1},{"header":"Getting Started","text":"Get started with LyteNyte Grid, a modern React data grid designed for enterprise-scale data challenges. Built in React, for React, it enables developers to ship faster and more efficiently than ever before.\nNo wrappers. No dependencies. Open code.","link":"docs/intro-getting-started","depth":1},{"header":"Getting Support","text":"Support is not an afterthought at 1771 Technologies. It is vital to our mission of building products that just work. This guide describes your support options so you can get fast, reliable help.","link":"docs/intro-getting-support","depth":1},{"header":"Installation With Shadcn","text":"Install LyteNyte Grid using the shadcn CLI. 1771 Technologies maintains a public registry of preconfigured LyteNyte Grid components styled for shadcn.","link":"docs/intro-installation-shadcn","depth":1},{"header":"Installation","text":"Install LyteNyte Grid with a package manager or import its assets from a CDN.","link":"docs/intro-installation","depth":1},{"header":"License Activation","text":"To activate LyteNyte Grid, set the license key in the configuration object.","link":"docs/intro-license-activation","depth":1},{"header":"Accessibility","text":"Implement ARIA standards and best practices to build inclusive, accessible data grids.","link":"docs/accessibility","depth":1},{"header":"Keyboard","text":"Navigate, select, and edit data using keyboard shortcuts and touch gestures.","link":"docs/keyboard","depth":1},{"header":"RTL Support","text":"Enable right-to-left rendering with a single configuration option.","link":"docs/rtl-support","depth":1},{"header":"Paginated Row Filtering","text":"Use the server data source to filter rows on the server and return only matching rows for each page request.","link":"docs/paginated-rs-row-filtering","depth":1},{"header":"Paginated Row Sorting","text":"Use the server data source to send a sort model to the server, which sorts the rows before returning each ordered page.","link":"docs/paginated-rs-row-sorting","depth":1},{"header":"Paginated Rows","text":"Use the server data source to paginate rows using a cursor-based offset to load rows one page at a time.","link":"docs/paginated-rs-rows","depth":1},{"header":"Row & Column Pivots","text":"Configure pivot row and column fields in the client data source to create specific views of your data.","link":"docs/pivoting-columns-and-rows","depth":1},{"header":"Pivot Filters","text":"Filter pivot rows using specific labels or predicate conditions.","link":"docs/pivoting-filtering","depth":1},{"header":"Grand Totals","text":"Summarize all pivot data with a grand total row. Configure the grid to display this summary at either the top or bottom of the viewport.","link":"docs/pivoting-grand-totals","depth":1},{"header":"Measures","text":"Configure measures to aggregate values across pivot rows and columns.","link":"docs/pivoting-measures","depth":1},{"header":"Pivoting Overview","text":"Summarize complex data relationships through pivoting and aggregation to reveal patterns and trends.","link":"docs/pivoting-overview","depth":1},{"header":"Pivot Sorting","text":"Pivot columns support sorting. Sort rows in ascending or descending order based on any pivot column.","link":"docs/pivoting-sorting","depth":1},{"header":"Pivot State Persistence","text":"Enabling pivots dynamically creates columns. Control pivot state to customize the pivot configuration to your requirements.","link":"docs/pivoting-state-persistence","depth":1},{"header":"Bundling & Tree Shaking","text":"LyteNyte Grid takes full advantage of automatic tree shaking, ensuring your final bundle includes only the components you use. This guide explains best practices to help your bundler exclude unused code.","link":"docs/prodready-bundling","depth":1},{"header":"Grid Versioning","text":"LyteNyte Grid uses semantic versioning to manage new grid releases.","link":"docs/prodready-grid-versioning","depth":1},{"header":"Security","text":"1771 Technologies implements comprehensive security measures to ensure the safe integration of LyteNyte Grid within your web applications.","link":"docs/prodready-security","depth":1},{"header":"Supported Browsers","text":"LyteNyte Grid is designed to work seamlessly across all popular desktop and mobile browsers. Our comprehensive testing ensures consistent functionality and performance across these supported platforms.","link":"docs/prodready-supported-browsers","depth":1},{"header":"TypeScript","text":"LyteNyte Grid offers first-class TypeScript support. This guide outlines best practices for extending LyteNyte Grid's column and API interfaces and avoiding errors through proper type checking.","link":"docs/prodready-typescript","depth":1},{"header":"Row Master Detail","text":"Create expandable detail sections beneath rows using customizable React components, expansion controls, and programmatic API methods.","link":"docs/row-detail","depth":1},{"header":"Row Dragging","text":"Drag and drop rows within the same LyteNyte Grid, across multiple grids, or into external drop zones and applications.","link":"docs/row-dragging","depth":1},{"header":"Row Full Width","text":"Create full width rows in LyteNyte Grid that render a single cell spanning the entire viewport.","link":"docs/row-full-width","depth":1},{"header":"Row Height","text":"LyteNyte Grid allows developers to control row height. Rows can have a fixed height, vary in height per row, or expand to fill the available viewport space.","link":"docs/row-height","depth":1},{"header":"Rows Overview","text":"Learn how the LyteNyte grid creates, renders, and manages rows. LyteNyte Grid has three types of rows, group rows, aggregated rows, and leaf rows. These rows define the grid's main content.","link":"docs/row-overview","depth":1},{"header":"Row Pinning","text":"LyteNyte Grid can freeze rows at the top or bottom of the grid viewport. Pinned rows remain visible as the user scrolls.","link":"docs/row-pinning","depth":1},{"header":"Row Spanning","text":"Cells can span multiple rows in LyteNyte Grid. When a cell spans, it occupies the rows beneath it, and the grid does not render the cells it covers.","link":"docs/row-spanning","depth":1},{"header":"Row Selection","text":"Select individual or multiple rows using LyteNyte Grid's row selection system. Easily implement checkbox-based selection with support for bulk operations.","link":"docs/row-selection","depth":1},{"header":"Cell Editing Server Data","text":"Use the server data source to edit cell values and synchronize those updates with the server.","link":"docs/server-data-loading-cell-editing","depth":1},{"header":"Handling Load Failures","text":"Use the server data source's built-in error recovery mechanisms to manage and retry failed network requests.","link":"docs/server-data-loading-handling-load-failures","depth":1},{"header":"Data Interface","text":"Use LyteNyte Grid's server data loading APIs to fetch and render row data from your backend.","link":"docs/server-data-loading-interface","depth":1},{"header":"Optimistic Loading","text":"Prefetch and optimistically load anticipated rows into the server data source to reduce loading indicators.","link":"docs/server-data-loading-optimistic-loading","depth":1},{"header":"Server Row Overview","text":"Use the server row data source to load and manage server-side row data in LyteNyte Grid.","link":"docs/server-data-loading-overview","depth":1},{"header":"Data Pushing or Pulling","text":"Control data loading with the server data source and use hybrid patterns where row data comes from both the client and the server.","link":"docs/server-data-loading-push-and-pull","depth":1},{"header":"Server Row Data","text":"LyteNyte Grid's server data source loads row data in slices and retrieves it on demand.","link":"docs/server-data-loading-row-data","depth":1},{"header":"Server Row Filtering","text":"Send a custom filter model to the server to filter rows before returning them to the client.","link":"docs/server-data-loading-row-filtering","depth":1},{"header":"Server Row Grouping","text":"Display hierarchical data by using the server data source to request and assemble data slices for grouped views.","link":"docs/server-data-loading-row-grouping-and-aggregation","depth":1},{"header":"Server Row Pinning","text":"Pin rows to the top or bottom of the viewport to keep them visible. With the server data source, the server returns these in a pinned-row response.","link":"docs/server-data-loading-row-pinning","depth":1},{"header":"Server Row Sorting","text":"LyteNyte Grid requests sorted rows by sending a defined sort model. The server applies the sort and returns the requested data slice.","link":"docs/server-data-loading-row-sorting","depth":1},{"header":"Data Updates","text":"To handle live data updates and display real-time views, the server data source requests update ticks at a configurable interval.","link":"docs/server-data-loading-row-updating","depth":1},{"header":"Unbalanced Rows (Tree Data)","text":"Use the server data source to handle unbalanced hierarchies and load hierarchical data on demand.","link":"docs/server-data-loading-unbalanced-rows","depth":1},{"header":"Grid Theming With CSS Modules","text":"LyteNyte Grid can be styled using CSS modules. CSS modules provide scoped class names for writing isolated CSS.","link":"docs/grid-theming-css-modules","depth":1},{"header":"Grid Theming With Emotion","text":"LyteNyte Grid can be styled using Emotion CSS (or any other CSS-in-JS library). This guide explains an approach compatible with styled-component style APIs.","link":"docs/grid-theming-emotion","depth":1},{"header":"Grid Theming With Tailwind","text":"Tailwind can be used to style LyteNyte Grid. This guide explains how to set up Tailwind and apply it to grid elements.","link":"docs/grid-theming-tailwind","depth":1},{"header":"Grid Theming","text":"LyteNyte Grid is a headless data grid. This guide explains how to theme LyteNyte Grid using vanilla CSS or pre-built themes to create a visually polished data grid.","link":"docs/grid-theming","depth":1},{"header":"Tree Editing","text":"Edit the nested tree data object using the grid's cell editing capabilities.","link":"docs/tree-source-data-editing","depth":1},{"header":"Tree Data","text":"Pass a nested JavaScript object to the tree data source to create a hierarchical grid layout.","link":"docs/tree-source-data","depth":1},{"header":"Tree Filtering","text":"Filter tree rows by passing a predicate function to the tree data source. The predicate runs for each row and determines whether the grid includes it.","link":"docs/tree-source-filtering","depth":1},{"header":"Tree Overview","text":"Use the tree data source to represent parent-child relationships in hierarchical data.","link":"docs/tree-source-overview","depth":1},{"header":"Tree Row Pinning","text":"Pin rows to the top or bottom of the viewport while using the tree data source. Pinned rows remain visible as users scroll through hierarchical data.","link":"docs/tree-source-pinning","depth":1},{"header":"Tree Sorting","text":"Sort tree rows in ascending or descending order by providing a single sort function or an array of sort dimensions to the hook.","link":"docs/tree-source-sorting","depth":1},{"header":"v2.0.3 (Latest)","text":"The latest changelog for LyteNyte Grid. Track new features, improvements, and changes with each release.","link":"docs/changelog/latest","depth":1},{"header":"v0.9.4","text":"The changelog of the LyteNyte Grid version v0.9.4.","link":"docs/changelog/v094","depth":1},{"header":"v1.0.18","text":"The latest changelog for LyteNyte Grid. Track new features, improvements, and changes with each release.","link":"docs/changelog/v1","depth":1},{"header":"API Reference","text":"The API Reference provides technical details and type descriptions for the LyteNyte Grid's public interface.","link":"docs/reference","depth":1},{"header":"Column","text":"Columns define how the grid renders content. This reference lists supported column properties.","link":"docs/reference/column","depth":1},{"header":"Grid API","text":"Use the API methods to query grid state and run operations programmatically.","link":"docs/reference/grid-api","depth":1},{"header":"Grid Props","text":"This reference lists the props you can pass to the LyteNyte Grid component.","link":"docs/reference/grid-props","depth":1},{"header":"Grid Types","text":"LyteNyte Grid provides TypeScript support. This reference details the main exported types for building type-safe grid implementations.","link":"docs/reference/grid-types","depth":1},{"header":"Layout","text":"As a headless data grid, LyteNyte grid positions cells and headers based on a layout description. This reference describes the types that make up the grid layout description.","link":"docs/reference/layout","depth":1},{"header":"Rows","text":"This reference details the three row types supported by LyteNyte Grid: leaf, group, and aggregated.","link":"docs/reference/row","depth":1},{"header":"Client Row Source","text":"LyteNyte Grid's client row source extends the base row source with additional capabilities for client-side data.","link":"docs/reference/client-row-source","depth":1},{"header":"Row Source","text":"This reference details the base row source type. All LyteNyte Grid row sources extend this type.","link":"docs/reference/row-source","depth":1},{"header":"Server Row Source","text":"LyteNyte Grid's server row source extends the base row source with additional capabilities for server-side data loading.","link":"docs/reference/server-row-source","depth":1},{"header":"Tree Row Source","text":"LyteNyte Grid's tree row source extends the base row source with additional capabilities for viewing object-based data.","link":"docs/reference/tree-row-source","depth":1},{"header":"Piece Utility","text":"Use the piece utility to create a stable reference to reactive state that can be passed as a prop without causing re-renders.","link":"docs/reference/piece-utility","depth":1},{"header":"Utility Functions","text":"Use LyteNyte Grid's exported utilities to enhance grid capabilities or to drive your application logic","link":"docs/reference/utility-functions","depth":1},{"header":"Expression Filters","text":"Create complex grid filters using a custom filter language with LyteNyte's expression capabilities.","link":"docs/expression-filters","depth":1},{"header":"Expression Plugins","text":"Expression plugins allow you to extend the syntax and evaluation capabilities of expressions provided by LyteNyte.","link":"docs/expression-plugins","depth":1},{"header":"Expressions Overview","text":"Create compound calculations using a domain-specific language that suits your application.","link":"docs/expressions-overview","depth":1},{"header":"Edit Update Method","text":"Edit Update Method You can edit cells in bulk using the editUpdateRows method on the grid's API. The editUpdateRows method requires a map of rows to update. The update map can use two key types: number: Represents the row index of the row that should be updated.string: Represents the row ID of the row that should be updated. When you provide a number key, LyteNyte Grid resolves the row index to its row ID. Avoid setting both the row index and row ID for the same row in the map. The demo below uses the editUpdateRows method to update the Price column in bulk. ::demo[Bulk Cell Editing=\"./demos/cell-editing-bulk\"] The code for increasing the price is shown below. Notice that the code creates a new update map, then calls editUpdateRows to update the rows through LyteNyte Grid's edit functionality.","link":"docs/cell-editing-bulk-editing#edit-update-method","depth":2},{"header":"Bulk Update vs. Direct Row Update","text":"Bulk Update vs. Direct Row Update At first glance, the editUpdateRows method appears to do the same thing as updating the rows in the grid directly. However, there is a distinction. When you update rows through editUpdateRows, LyteNyte Grid runs the same validations as if the user edited the cell directly. All rows must pass validation for LyteNyte Grid to apply the bulk update. The editUpdateRows method returns the row validation result, which you can use to provide feedback if the update fails.","link":"docs/cell-editing-bulk-editing#bulk-update-vs-direct-row-update","depth":3},{"header":"Bulk Editing Cells","text":"Bulk Editing Cells You can use the editUpdateCells method to bulk update a set of cells in the grid, rather than updating entire rows. The editUpdateCells method is a convenience wrapper that uses editUpdateRows internally. LyteNyte Grid converts the provided cell updates into row updates and then calls editUpdateRows. The demo below demonstrates editUpdateCells by implementing standard clipboard functionality, including copy, cut, and paste. See the Clipboard guide for more details. Click and drag to select a range of cells. Press Ctrl C to copy, Ctrl X to cut, and Ctrl V to paste. The demo does not validate pasted values. For validation, see the Cell Editing Validation guide. ::demo[Clipboard Bulk Editing=\"../(export)/demos/clipboard\"] :::note The clipboard demo uses cell range selection, which is a feature. However, the editUpdateCells method is available in the Core edition of LyteNyte Grid. :::","link":"docs/cell-editing-bulk-editing#bulk-editing-cells","depth":2},{"header":"Full Row Edits","text":"Full Row Edits Set the editMode property to \"row\" to enable row-based cell editing. When row edit mode is active, LyteNyte Grid updates cells on a per-row basis. When a row edit begins, the grid renders all editable cells in the row. The demo below shows row edit mode. The Price, Customer, and Email columns are editable. Double-clicking a cell initiates row editing and renders edit components for all editable cells in that row. ::demo[Row Edit Mode=\"./demos/cell-editing-row\"] When a row edit is active, pressing the Tab key cycles through the row's editable cells. If row edit mode is not active, pressing Tab ends the edit when the edit renderer loses focus.","link":"docs/cell-editing-full-row#full-row-edits","depth":2},{"header":"Row Edit vs Cell Edit","text":"Row Edit vs Cell Edit The choice between individual cell edits and full row edits depends largely on the type of data you are editing. Row editing works best when editing one cell often requires editing another cell in the same row. Individual cell editing is better for general updates, since each cell change is validated and committed independently. Using row edits instead of cell edits has no performance penalty or functional restrictions. The choice depends entirely on your use case.","link":"docs/cell-editing-full-row#row-edit-vs-cell-edit","depth":3},{"header":"Editing Linked Cells","text":"Editing Linked Cells A standard edit renderer calls the changeValue method on the EditParams passed to the renderer. The changeValue method updates only the edit value for the cell currently being edited. To create a linked cell edit, use the changeData method instead, and provide new edit values for all cells that should update when the edited cell changes. In the demo below, the Product column is linked to the Price column. Selecting a new product automatically updates the Price to that product's default value. Linked edits don't have to be bidirectional. Editing Product updates Price, but editing Price does not change the selected product. ::demo[Updating Linked Cells=\"./demos/cell-editing-popover-smart\"] :::tip A cell edit does not have to update another editable cell. You can create linked edits that update cells which are not themselves editable. :::","link":"docs/cell-editing-linked-cell-edits#editing-linked-cells","depth":2},{"header":"Text Editors","text":"Text Editors A text edit renderer uses a element. In the demo below, double-click a cell in the Customer column to begin text editing. ::demo[Text Edit Renderer=\"./demos/cell-editing-text-renderer\"] The text editor in the demo uses the controlled pattern to manage the input value. Since the editor edits text, it does not require custom parsing. The code below updates the value directly:","link":"docs/cell-editing-renderers#text-editors","depth":2},{"header":"Number Editors","text":"Number Editors Number edit renderers can use an input with type number, but text inputs with custom number parsing offers better control over the input experience. The demo below uses Ark UI's number input, which enforces numeric-only values, with improved number input editing. ::demo[Number Edit Renderer=\"./demos/cell-editing-number-renderer\"]","link":"docs/cell-editing-renderers#number-editors","depth":2},{"header":"Date Editors","text":"Date Editors Editing requirements for dates vary from simple selections to complex ranges and timezone adjustments. For simple edits, the native browser's date input is an efficient choice. Double-click a cell in the Purchase Date column to start editing. ::demo[Date Edit Renderer=\"./demos/cell-editing-date\"] The date edit renderer uses a native date input as an uncontrolled component instead of a controlled one. Controlling browser date inputs with React state can cause invalid states during manual keyboard entry. To avoid this, keep the date input uncontrolled and track changes. The browser enforces date validity, and the edit state reflects the latest value.","link":"docs/cell-editing-renderers#date-editors","depth":2},{"header":"Popover Editors","text":"Popover Editors For columns with a fixed set of values, a dropdown list is a common editing pattern. While the native select element is the simplest option, it has several limitations: Programmatic Control: You can't force the select menu to open when editing begins; users must press Space or Enter.Limited Styling: option elements support minimal CSS customization, which makes consistent styling harder.Poor Scalability: select element works best with a small set of options and becomes impractical for large lists of values. To see the native select element in action, double-click any cell in the Product column below to begin editing. ::demo[Native Select Popover=\"./demos/cell-editing-popover\"] Given the drawbacks of the select element, a better alternative is to use a combobox or custom select component. The demo below demonstrates this using LyteNyte Grid's Smart Select component. Double-click any cell in the Product column below to begin editing. ::demo[Smart Select Popover=\"./demos/cell-editing-popover-smart\"] :::note LyteNyte Grid's Smart Select component is only available to users. However, you can use another open-source combobox component. Below are some good options: Headless UI ComboboxBase UI ComboboxReact Aria Combobox :::","link":"docs/cell-editing-renderers#popover-editors","depth":2},{"header":"Popover Editor Considerations","text":"Popover Editor Considerations When you create a custom popover, consider these points: Enter: LyteNyte Grid commits edits on Enter. Many comboboxes use Enter to open the completion menu. Decide which action Enter triggers while editing.ArrowDown: LyteNyte Grid moves focus to the next cell on ArrowDown. Many select components use ArrowDown to open options. Decide which action ArrowDown triggers while editing.Clipping: Grid cells clip content that exceeds their bounds. To prevent your popover from being clipped, use a React Portal to render it into the body element.Input Triggers: By default, editing starts when a user presses a printable character. If this isn't ideal for your selection component, set editOnPrintable to false on the column.","link":"docs/cell-editing-renderers#popover-editor-considerations","depth":3},{"header":"Edit Validation","text":"Edit Validation Set the editRowValidatorFn property on the grid to validate cell edit updates. LyteNyte Grid calls editRowValidatorFn at the row level, even for single-cell edits. The function must return one of these values: true: Validation passed; the edit can safely be applied to the row data.false: Validation failed without details. Return false only if your UI displays validation feedback in another way.Record: A validation error map, keyed by an error code you define, with an error message or error object as the value. In the demo below, an editRowValidatorFn function validates that the value in the Price column is greater than 0. Double-click any cell in the Price column to begin editing. When the value is less than or equal to 0, LyteNyte Grid displays an error tooltip. ::demo[Edit Validation=\"./demos/cell-editing-edit-validation\"] The demo code validates the Price column using the code shown below. You can use editRowValidatorFn to validate any type of data and integrate any validation library. :::tip Zod is a TypeScript-first schema validation library. Consider using Zod to validate cell edits and get strong type safety. :::","link":"docs/cell-editing-validation#edit-validation","depth":2},{"header":"Basic Cell Editing","text":"Basic Cell Editing Set editMode to \"cell\" to enable single-cell editing. To make a column editable, set these column definition properties: editable: Boolean or predicate function that returns whether the cell is editable. Use a predicate to control editability per row.editRenderer: Component to render in the cell while editing. The grid passes EditParams to this component to handle edits. LyteNyte Grid treats row data as immutable. When a user edits a cell, the grid triggers the onRowDataChange callback. Use this prop to update your data source. This guide demonstrates cell editing using the client row source. In the demo below, the Price and Customer columns are editable. To edit a value, double-click any cell in either column. ::demo[Basic Cell Editing=\"./demos/cell-editing-basic\"] :::note The demo omits validation to focus on basic editing. For example, it allows negative prices. See the Cell Edit Validation guide for best practices. ::: The demo defines basic edit renderers for the Price and Customer columns. For custom renderers, see the Cell Edit Renderers guide. The grid passes EditParams props to each edit renderer. This demo uses: editValue: Current value being edited. It is initialized from the cell's value and applied only when you commit the edit.changeValue: Method that updates editValue during editing. The Customer column edit renderer assigns editValue to the value, and the input's onChange handler calls changeValue to update the edit value. The renderer also converts editValue to a string because the input element expects a string value, and editValue may not initially be a string.","link":"docs/cell-editing#basic-cell-editing","depth":2},{"header":"Edit Click Activator","text":"Edit Click Activator Use the editClickActivator property on the grid to change the click interaction that starts cell editing. The property accepts one of three values: \"single-click\": Begins cell editing when the user clicks an editable cell.\"double-click\": Begins cell editing when the user double-clicks an editable cell. This is the default.\"none\": Clicking an editable cell does not start editing. The demo below enables single-click editing on the Price and Customer columns. Click any cell in these columns to begin editing. ::demo[Single Click Edit=\"./demos/cell-editing-click-activator\"]","link":"docs/cell-editing#edit-click-activator","depth":3},{"header":"Edit Parameters","text":"Edit Parameters LyteNyte Grid provides each cell edit renderer with props conforming to the EditParams type. EditParams contains the state and methods required to manage cell updates. The following sections detail the most important properties of EditParams.","link":"docs/cell-editing#edit-parameters","depth":2},{"header":"Edit Value and Data","text":"Edit Value and Data To manage cell editing, distinguish between the editValue and editData properties on EditParams. When a cell edit begins, LyteNyte Grid creates a copy of the edited row's data and stores it in editData. The grid then uses the column's field property to derive the editValue for the active cell. The pseudo-code below illustrates this process: This pseudo-code does not reflect the exact implementation, but it illustrates the core relationship between editData and editValue. Use the changeValue method to update editValue. This method is a convenience wrapper around changeData that applies updates to a single cell. In contrast, changeData updates the edit data for the entire row. The following two calls are equivalent:","link":"docs/cell-editing#edit-value-and-data","depth":3},{"header":"Edit Setter","text":"Edit Setter Use the editSetter property on a column to control how the grid updates editValue when changeValue is called. By default, LyteNyte Grid uses the column's field property to update the edit value for a cell. This behavior works only when field refers to a string or numeric property. When field is a function or a path, you must define an editSetter so the grid can apply updates correctly. For example, the following column definition always title-cases the edited value: The editSetter must always return the full editData object, not just the modified value. Use editSetter to enforce update constraints and to create linked cell updates. For more information, see the Linked Cell Edits guide.","link":"docs/cell-editing#edit-setter","depth":3},{"header":"Edit Mutate Commit","text":"Edit Mutate Commit Use the editMutateCommit property on the column to write changes to the editData object before the grid completes the edit operation. The editMutateCommit property provides one final opportunity to adjust the row data before the grid fires edit events. For example, the following definition parses the column's value to ensure it is a number. The code mutates the editData object directly to change the final value of the edit. The grid calls editMutateCommit once for each column whenever any column value is edited.","link":"docs/cell-editing#edit-mutate-commit","depth":3},{"header":"Committing and Cancelling Edits","text":"Committing and Cancelling Edits LyteNyte Grid does not mutate the original row data during an edit. To end an edit programmatically, EditParams provides two methods: commit: Applies the new value via row update callbacks and ends the edit.cancel: Discards the edited value and ends the edit.","link":"docs/cell-editing#committing-and-cancelling-edits","depth":3},{"header":"Edit Events","text":"Edit Events LyteNyte Grid fires these events during the cell editing lifecycle. For the full list, see the grid props API reference. onEditBegin: Fires when a cell edit begins. Call preventDefault on the event params to prevent the edit.onEditEnd: Fires when editing ends and the grid is about to commit the edited value. Call preventDefault on the event params to prevent the commit.onEditCancel: Fires when editing ends without applying the edited value.onEditFail: Fires when validation fails during editing.","link":"docs/cell-editing#edit-events","depth":2},{"header":"Programmatic Editing","text":"Programmatic Editing LyteNyte Grid's API exposes methods for programmatically editing cells. For complete details, see the grid API reference. editBegin: Begins editing a specific cell.editEnd: Ends the edit and commits the value. Pass cancel to cancel instead of commit.editIsCellActive: Returns true if the specified cell is currently being edited.editUpdate: Updates rows in bulk. See the Bulk Cell Editing guide for details. :::note Use programmatic editing APIs only for advanced scenarios. For basic cell editing, use LyteNyte Grid's built-in editing behavior. :::","link":"docs/cell-editing#programmatic-editing","depth":2},{"header":"Flashing Cells On Change","text":"Flashing Cells On Change To flash a cell when its value changes, compare the current and previous values. If the values differ, apply a CSS flash animation. Click Update Data to randomly change cell values. ::demo[Flash Updated Cells=\"./demos/cell-flashing\"] The cell flashing logic lives in the NumberCell component. NumberCell is a custom cell renderer that the grid columns use. The component tracks the previous value and applies the \"flash\" animation in an effect whenever the value changes.","link":"docs/cell-diff-flashing#flashing-cells-on-change","depth":2},{"header":"Enhanced Flash","text":"Enhanced Flash Flashing the cell background indicates a value changed but doesn't show the change's direction or magnitude. To provide context, flash the value itself, as shown in the demo below. ::demo[Flash Value Changes=\"./demos/cell-flashing-enhanced\"] This demo reuses the change-detection logic from the previous example but alters the rendering. Instead of flashing the background, the cell renderer highlights the value change and then fades out the effect. See the code below.","link":"docs/cell-diff-flashing#enhanced-flash","depth":3},{"header":"Defining a Cell Renderer","text":"Defining a Cell Renderer Set the cellRenderer property on a column to provide the column with a component to use when rendering the content of that cell. The cellRenderer property is a React function component that receives a set of props from LyteNyte Grid. These props conform to the Grid.T.CellRendererParams type. The demo below assigns a custom cell renderer for each column. Click Expand Code to see that each column has its cellRenderer property set. The components.tsx file defines the renderer components. ::demo[Custom Cell Renderers=\"../(column)/demos/column-overview-demo\"] Cell renderers are React components. They render within your application's React tree and can read any existing React context. The Grid.T.CellRendererParams type provides the most common properties for cell rendering. Additional state can be provided by extending the grid's API or creating a context value for use in the cell. The following code implements the cell renderer for the Product column. LyteNyte Grid uses standard React components, so you don't need specialized grid APIs or patterns. You can leverage your existing React expertise without learning a proprietary API.","link":"docs/cell-renderers#defining-a-cell-renderer","depth":2},{"header":"Column Field","text":"Column Field Each cell derives its value from the column's field property. The grid handles data retrieval differently for leaf rows than for group or aggregated rows. When you define field as a function or path, the grid resolves the value dynamically rather than accessing the row data via a direct key. To simplify value retrieval, columnField returns the cell value for a specific row. In the demo below, the Balance column's cell renderer calls columnField to retrieve the cell value. ::demo[Cell Value=\"../(client-source)/demos/client-row-aggregation-fn\"] By calling api.columnField to retrieve the cell value, the renderer avoids branching on row type. See the code below.","link":"docs/cell-renderers#column-field","depth":3},{"header":"Cell State and Virtualization","text":"Cell State and Virtualization LyteNyte Grid uses virtualization by default. As you scroll, React unmounts off-screen rows and remounts them as they re-enter the viewport, resetting any local state. To persist client state across scrolls, lift that state above the cell renderer in the React tree. Use a parent component or a context provider rather than useState inside the renderer.","link":"docs/cell-renderers#cell-state-and-virtualization","depth":2},{"header":"Optimizing Cell Renderers","text":"Optimizing Cell Renderers LyteNyte Grid supports any cell renderer content. Follow these guidelines to maximize performance and usability: Keep Renderers Lightweight: Avoid expensive computations in the render path because they degrade scroll performance. The grid can render thousands of cells per update. Memoize complex calculations where possible.Derive State From Props: Use Grid.T.CellRendererParams to access row state. For example, use the selected property to render selection feedback rather than tracking state locally.Respect Cell Boundaries: Cells clip overflow by default. Ensure content fits within defined dimensions to avoid truncation.","link":"docs/cell-renderers#optimizing-cell-renderers","depth":2},{"header":"Single-Range Selection","text":"Single-Range Selection Set cellSelectionMode to \"range\" to enable single-range selection. Click a cell, then drag across the grid to select a range. ::demo[Single-Range Selection=\"./demos/cell-selection\"] To enable cell selection, the demo sets the cellSelectionMode property as shown in the code below.","link":"docs/cell-selection#single-range-selection","depth":2},{"header":"Uncontrolled Cell Selection","text":"Uncontrolled Cell Selection When cell selection is enabled, LyteNyte Grid tracks the selection state internally. Call cellSelections to retrieve the current selection. Uncontrolled cell selection works well for operations like copy-to-clipboard without managing selection state in your app. In the demo below, select a cell range and press Control+C or Command+C to copy to your clipboard. ::demo[Copy-to-Clipboard=\"./demos/cell-selection-copy\"] The demo listens for the viewport keyDown event, then: Retrieves the current selection bounds via api.cellSelections.Exports the selected range data using api.exportData, converts it to a string, and writes it to the clipboard.Applies copy-flash to highlight the selection. The implementation code is shown below.","link":"docs/cell-selection#uncontrolled-cell-selection","depth":3},{"header":"Controlled Cell Selection","text":"Controlled Cell Selection Some use cases require you to manage the cell selection state. Enable controlled selection by passing an array of ranges to the cellSelections prop. Update the selection state via the onCellSelectionChange callback. The demo below stores cellSelections in React state using useState. It passes the selection to a status bar that displays averages for numeric columns. ::demo[Selection Status Bar=\"./demos/cell-selection-status\"] The cellSelections property expects an array of DataRect objects. A DataRect defines a rectangular selection area using the following interface. The rowEnd and columnEnd values are exclusive and are not included in the selected cells. :::warn cellSelections refers to both an API method and a grid property: API Method (api.cellSelections): Returns the current selection rectangles.Property (cellSelections): Controls the selection state. In controlled selection mode, api.cellSelections returns the same value you pass to cellSelections. :::","link":"docs/cell-selection#controlled-cell-selection","depth":3},{"header":"Header Highlights","text":"Header Highlights Headers of columns containing selected cells receive the data-ln-cell-selected=\"true\" attribute. Use this attribute to style those headers. In the demo below, CSS highlights any header whose column contains a selected cell. ::demo[Column Header Highlights=\"./demos/cell-selection-header-highlight\"] The demo uses the LyteNyte Grid Tailwind plugin to apply the following Tailwind CSS class: \"ln-header:data-[ln-cell-selected=true]:bg-ln-primary-05\" If you are not using Tailwind, the class is equivalent to the CSS below:","link":"docs/cell-selection#header-highlights","depth":2},{"header":"Excluding Marker Column Selection","text":"Excluding Marker Column Selection The marker column is an internal column that is automatically pinned to the start of the grid. It does not appear in the columns array you pass to the grid. By default, selection ranges can include the marker column. Set cellSelectionExcludeMarker to true to prevent selection of this column. :::tip LyteNyte Grid also applies the data-ln-cell-selected attribute to rows that contain selected cells. Use the attribute for row-level styling. ::: ::demo[Exclude Marker Column=\"./demos/cell-selection-marker\"] Even with cellSelectionExcludeMarker set to true, the grid still reserves space for the marker column. When the marker column is enabled, it uses column index 0, so the first non-marker column always has column index 1.","link":"docs/cell-selection#excluding-marker-column-selection","depth":2},{"header":"Column and Row Selection","text":"Column and Row Selection To select an entire row or column, set the cellSelections prop to a selection rectangle that covers the target cells. This works regardless of cellSelectionMode. The demo below illustrates this: Column Selection: Click a header to select the entire column.Row Selection: Click a cell in the marker column to select an entire row. By default, LyteNyte Grid clears selection when focus moves to a non-cell element, such as a header cell. Set cellSelectionMaintainOnNonCellPosition to preserve selection when headers receive focus. ::demo[Column / Row Selection=\"./demos/cell-selection-column-and-row\"]","link":"docs/cell-selection#column-and-row-selection","depth":2},{"header":"Spanned Cell Selection","text":"Spanned Cell Selection When a selection includes a spanning cell, LyteNyte Grid expands the selection rectangle to cover the cell's full row/column span. In the demo below, selecting a spanning cell expands the selected area accordingly. ::demo[Cell Selection With Spans=\"./demos/cell-selection-spans\"]","link":"docs/cell-selection#spanned-cell-selection","depth":2},{"header":"Pinned Area Selection","text":"Pinned Area Selection Rows and columns can be pinned to the edges of the grid, keeping them visible while the rest of the grid scrolls. When you drag a selection from the scrollable area toward a pinned area, the grid includes pinned cells only after the viewport reaches the pinned edge. This ensures the selection range remains continuous: Pinned Start/Top: The grid must be scrolled to the start or top.Pinned End/Bottom: The grid must be scrolled to the end or bottom. As you drag toward the edge, LyteNyte Grid automatically scrolls the viewport. Once the viewport reaches the pinned edge, the selection expands to include the pinned cells. These constraints only apply when the selection begins outside the pinned area. The demo includes pinned rows at the top and bottom, and columns at the start and end. Select different areas to see how selection expands into pinned regions. ::demo[Pinned Cell Selection=\"./demos/cell-selection-pins\"] When a selection spans pinned areas, the selection rectangle appears visually split. This split indicates that the selection crosses both pinned and scrollable viewport regions. Despite the visual separation, the cellSelections state contains a single, continuous selection rectangle.","link":"docs/cell-selection#pinned-area-selection","depth":2},{"header":"Multi-Range Selection","text":"Multi-Range Selection Set cellSelectionMode to \"multi-range\" to enable multiple selection ranges. Add Range: Hold Control (or Command) and drag to add a new range.Deselect Area: Hold Control (or Command) and start a selection inside an existing range to deselect that area. ::demo[Multiple Range Selections=\"./demos/cell-selection-multi-range\"] Multiple ranges are useful for comparing values across separate cell groups. However, they complicate operations like copying. When multiple ranges exist, the cellSelections state stores multiple selection rectangles, which can make it unclear which range to copy. Use \"range\" mode unless your application specifically requires multiple ranges.","link":"docs/cell-selection#multi-range-selection","depth":2},{"header":"Styling Cell Selections","text":"Styling Cell Selections LyteNyte Grid renders inert div overlays to visualize selections. These elements are unstyled by default. To style selection rectangles, target their data attributes. The CSS below illustrates the approach used by built-in themes. For more information, refer to the Grid Theming guide.","link":"docs/cell-selection#styling-cell-selections","depth":2},{"header":"Cell Tooltips","text":"Cell Tooltips The demo below renders a Radix UI tooltip component. Hover over a cell in the Symbol column to view the symbol's network. Use this pattern to integrate any auxiliary component into your cells. ::demo[Cell Tooltips=\"./demos/cell-with-tooltip\"] This implementation matches standard Radix UI documentation. LyteNyte Grid requires no wrappers or overrides, ensuring compatibility with any React tooltip component.","link":"docs/cell-tooltips#cell-tooltips","depth":2},{"header":"Cell Popovers","text":"Cell Popovers You can also use a popover to show additional details. In the demo below, focusing a cell in the Symbol column opens a popover with the symbol's network. ::demo[Cell Popovers=\"./demos/cell-with-popover\"] Instead of rendering the popover within a custom cell renderer, this demo renders it as a grid sibling. On cell focus, React state stores the focused cell element and network name, and the demo shows the popover when that state is set. To implement popovers, you can also extend the grid API with an imperative method or manage popover behavior via React context. LyteNyte Grid is unopinionated, supporting the architecture that best fits your requirements. :::info The demo uses LyteNyte Grid's popover component, exclusive to LyteNyte Grid PRO. You can replace it with any popover component. For details on the built-in component, see the Popover guide. :::","link":"docs/cell-tooltips#cell-popovers","depth":2},{"header":"Aggregations","text":"Aggregations In LyteNyte Grid, an aggregation is a record of key-value pairs, where each key is usually a column id, and each value is the result of an aggregation function such as sum or average. This concept of an aggregation is reflected in the data type required by a row group node and row aggregation node. These nodes must use object data, unlike leaf rows, which may use any data type. To compute an aggregation using the client data source, pass one of the following to the useClientDataSource: An aggregation functionA set of aggregation dimensions","link":"docs/client-source-aggregations#aggregations","depth":2},{"header":"Aggregation Functions","text":"Aggregation Functions An aggregation function receives a set of leaf rows to aggregate. The function must return an object containing all computed aggregation values. In the demo below, the aggregation function computes the count of unique values for text columns and the average for number columns. The demo passes this aggregation function to the aggregate property of the useClientDataSource hook. ::demo[Function Aggregate Rows=\"./demos/client-row-aggregation-fn\"] The aggregation function performs the full aggregation for all columns in the grid. The code below shows an example implementation. Notice how this logic can become repetitive. For this reason, LyteNyte Grid provides aggregation dimensions to simplify computing aggregations in a more declarative way.","link":"docs/client-source-aggregations#aggregation-functions","depth":3},{"header":"Aggregation Dimensions","text":"Aggregation Dimensions An aggregation dimension is an object with a dim property that contains an id value and optionally a field, and an fn property that references an aggregator function. The full type is shown below: Here are some examples of aggregation dimensions: The fn property is an Aggregator or a string that references a registered Aggregator function. An Aggregator function computes a single aggregated value. An Aggregator has the following type: Since the field parameter of an Aggregator can be a dynamic type, LyteNyte Grid exports the computeField utility function. computeField returns the value of the registered field for a given leaf row. The computeField utility returns unknown by default. You can cast the return value by providing a generic parameter to the function. If you provide a type, you must ensure the returned value conforms to the specified type. The computeField utility will not validate the type for you. :::note Do not confuse Aggregators with the AggregationFn function you provide to the useClientDataSource hook. An Aggregator computes a single value that is assigned to a single key. An AggregationFn produces the full aggregation result object. ::: When an aggregation dimension uses a string fn, the client data source looks up the corresponding Aggregator from the grid's registered aggregators. Register aggregators on the aggregateFns property of the grid. For example, you can define a summation aggregator as follows: In the demo below, aggregation dimensions compute the aggregation value for each group row. The demo registers two aggregators ahead of time: same: Returns a value if all aggregated values are the same; otherwise returns null.avg: Returns the average of the aggregated values. ::demo[Client Row Aggregation Dimensions=\"./demos/client-row-aggregation-dimensions\"]","link":"docs/client-source-aggregations#aggregation-dimensions","depth":3},{"header":"Updating Aggregations","text":"Updating Aggregations LyteNyte Grid does not enforce a predefined aggregation model. You are free to extend the grid's API or column definitions to include one. This section guides you on extending the column specification to allow columns to specify an aggregation and define a list of allowed aggregations. Aggregations encompass arbitrary computations over row-level data, including advanced statistical and windowed operations such as standard deviation and rolling averages. Start by creating an updated column specification. In this example, the fields are mandatory, but you can choose a type that matches your use case. This example uses strings for aggregations, since the demo registers all possible aggregations ahead of time. Text columns support four aggregators: samefirstlastcount Number columns support seven aggregators as well: avgsumcountmaxminfirstlast In the demo below, the aggregators are created and added to the client data source. The column headers use a custom header renderer to let you change the aggregation applied to a column based on the allowedAggs list. ::demo[Row Group Aggregation Updates=\"./demos/client-row-aggregation-updates\"] The demo extends the columns with the agg and allowedAggs properties. The demo then derives aggregation dimensions from the columns to create the value passed to the useClientDataSource hook. The code below shows this derivation:","link":"docs/client-source-aggregations#updating-aggregations","depth":2},{"header":"Data Property","text":"Data Property Use the data property in the useClientDataSource hook to provide the raw data for creating rows in the client data source. The example below shows a basic configuration. ::demo[Client Data Setup=\"./demos/client-overview\"]","link":"docs/client-source-data#data-property","depth":2},{"header":"Updating Data","text":"Updating Data LyteNyte Grid's useClientDataSource derives the view from the data you provide. This means that changes to the data value you pass to the hook will update the rows in the grid. useClientDataSource updates row nodes when the data changes and creates new nodes only for rows that changed. Update efficiently by passing a new data array that keeps unchanged rows and replaces only the modified ones. The demo below shows direct data updates. Every second, it randomly updates a subset of visible rows. ::demo[Direct Data Updates=\"./demos/client-data-update\"] To create the ticking data, the demo stores the data in React state and updates it every second by calling setData with a new data set, as shown below. LyteNyte Grid updates only the changed rows, making it highly efficient for large-scale data updates. In testing, the grid handles over 10,000 updates per second, even with filtering and grouping enabled. Still, you should optimize row updates. Frequent updates can trigger unnecessary work. Consider these tips: Perform Delta Updates: If possible, reuse unchanged row objects in your data array instead of creating new object references for every row. This lets LyteNyte Grid detect which rows changed and create new row nodes only for those rows.Batch Update Row Data: LyteNyte Grid can handle updates with millisecond-level precision, imperceptible to humans. Batching updates to your row data into intervals of 200 milliseconds to a few seconds reduces render churn and improves performance.","link":"docs/client-source-data#updating-data","depth":2},{"header":"Filter Property","text":"Filter Property The filter property on the client row source accepts a filter predicate or an array of filter predicates. A filter predicate is a function that receives a leaf row and returns true if the grid should keep the row or false if the grid should filter the row out. A basic filter function is shown below. Click the Mastercard button to view only Mastercard payments, or the Visa button to view only Visa payments. ::demo[Client Row Filter Function=\"./demos/client-filtering\"] The demo creates a filter function that is passed to the client source, as shown below. This minimal approach supports any filter model, since filtering is an arbitrary predicate function.","link":"docs/client-source-filtering#filter-property","depth":2},{"header":"Filter Array","text":"Filter Array Providing a single filter function is cumbersome when filters originate from different sources. For example, if you support per-column text search, you can write one filter function per column and pass those column filter functions to useClientDataSource. ::demo[Filter Function Array=\"./demos/client-filtering-array\"]","link":"docs/client-source-filtering#filter-array","depth":3},{"header":"Filter Leaf Rows","text":"Filter Leaf Rows Filter callbacks passed via the filter property receive leaf rows only because filtering runs before grouping or aggregation. Apply filters to raw rows, not grouped or aggregated output. For post-grouping filtering in the client source, see the Client Row Having Filter guide.","link":"docs/client-source-filtering#filter-leaf-rows","depth":2},{"header":"Applying Having Filters","text":"Applying Having Filters Having filters target aggregated group rows in the client data source. Review the Client Row Grouping and Client Row Aggregations guides for prerequisite knowledge. To apply a having filter, set having on the client data source to an array of having filter functions (HavingFilterFn | null[ ]). The grid uses the function at each index to filter group rows at that depth. Use null to skip filtering at a depth. The demo demonstrates a HavingFilterFn utilizing the filterModel provided by the grid API extension. The filter model lets you build a filter representation before generating the filter function from it. The demo uses a custom floating header row component that allows users to modify the model's value. In the demo, the having filter applies to the second row grouping level: Education. For example, when you set the Balance filter to greater than $0, the grid removes the Secondary row from the Administration group and the Primary row from the Blue-Collar group. ::demo[Having Filter Function=\"./demos/client-row-having\"] The demo creates the HavingFilterFn function by iterating through the filters in the model and applying them one at a time.","link":"docs/client-source-having-filters#applying-having-filters","depth":2},{"header":"Label Filter Function","text":"Label Filter Function The LyteNyte Grid client data source accepts an array of LabelFilter functions or null values. The array index maps to a group depth: element n is applied at depth n. If an element is null, the client data source skips label filtering at that depth. The demo shows label filters in action. Click a pill to exclude that label from the grid. Each pill row maps to a group level: The Job pill row filters job groups.The Education pill row filters education groups. The demo below shows label filters in action. Clicking a pill disables that label in the grid. The pills are arranged in rows to represent the grouping level. For example, the Job pills filter labels in the job grouping, and the Education pills filter labels in the education grouping. ::demo[Row Label Filters=\"./demos/client-row-label-filters\"] The demo scans pills to identify inactive items. For each grouping level that includes inactive pills, it creates label filters to remove rows with those labels. This is shown in the code below. For more on pill manager functionality, see the Pill Manager guide.","link":"docs/client-source-label-filters#label-filter-function","depth":2},{"header":"Client Row Source","text":"Client Row Source LyteNyte Grid exposes the useClientDataSource hook to create a grid row source for client data. A row source is an object that implements the RowSource interface shown below. You don't implement RowSource directly; LyteNyte Grid provides hooks that generate row sources for each supported data source type. This guide covers the Client Row Source. LyteNyte also includes these row sources: Server Row Source: Partially loading large datasets on demand from your backend.Tree Row Source: A client row source for row data stored as an object instead of an array.","link":"docs/client-source-overview#client-row-source","depth":2},{"header":"Using Client Row Source","text":"Using Client Row Source The useClientDataSource hook requires an array of data. Each item in the array becomes a row in the grid. Updates to the data property declaratively update the row source. The client row source is therefore derived state. The demo below provides a simple example of how to use the client row source. The remaining guides in this section explore the different capabilities of LyteNyte Grid's client data source. ::demo[Client Row Data Source=\"./demos/client-overview\"]","link":"docs/client-source-overview#using-client-row-source","depth":2},{"header":"Adding Rows","text":"Adding Rows In the demo below, click Add Top Row to insert a new row at the top of the grid, or Add Bottom Row to add a new row at the bottom. ::demo[Adding Client Rows=\"./demos/client-row-adding\"] Handling new row additions is straightforward. Store the existing row data in React state, then update the state via the onRowsAdded callback, as shown below. :::tip The useClientDataSource hook returns a row source that extends the RowSource interface with additional properties. When you use TypeScript, you can type the additional properties using the RowSourceClient type. The RowSourceClient type is a generic type that expects the GridSpec type, which creates a circular type definition. TypeScript can handle this pattern, but the feature to solve this is not well known. Use the this keyword in the type, as shown below: :::","link":"docs/client-source-row-adding#adding-rows","depth":2},{"header":"Deleting Rows","text":"Deleting Rows In the demo below, click the trash icon to delete a row from the grid. When you delete a row, the grid calls the onRowsDeleted callback that you pass to useClientDataSource. The client data source calls onRowsDeleted with two arguments: the rows to delete and their source indices. ::demo[Deleting Rows=\"./demos/client-row-deleting\"] The demo deletes rows using the code below. Notice that the code creates a new array by calling filter. You may think you could simplify this by using splice to delete a row. However, splice changes the indices of the existing array, which invalidates the source indices passed to onRowsDeleted. In this case, filter is a better choice. :::info A source index is the array index in the original data used to create the row node. A source index is not the same as a row index, because the row index can change based on the grid's sort, grouping, and filter state, whilst a source index is a stable lookup to the original row data. :::","link":"docs/client-source-row-deleting#deleting-rows","depth":2},{"header":"Full Tree Collapsing","text":"Full Tree Collapsing Setting rowGroupCollapseBehavior to \"full-tree\" collapses group rows that have a single child at every depth level. Use the \"full-tree\" setting when you want a grouping tree that is as flat as possible without losing information. ::demo[Full Tree Collapsing=\"./demos/client-row-grouping-collapsing\"] :::note To clearly demonstrate the \"full-tree\" collapse behavior, the sample data includes only a single row for the Blue-Collar job category. :::","link":"docs/client-source-row-group-collapsing#full-tree-collapsing","depth":2},{"header":"Last Only Collapsing","text":"Last Only Collapsing Setting rowGroupCollapseBehavior to \"last-only\" collapses only the final group row when it has a single child. Unlike \"full-tree\", the client data source does not collapse parent groups after collapsing the final group, as the demo below shows. ::demo[Last Row Group Collapsing=\"./demos/client-row-grouping-collapse-last\"] In the demo, the Blue-Collar group has a single child. However, because the grid uses two grouping levels, only the last level is collapsed.","link":"docs/client-source-row-group-collapsing#last-only-collapsing","depth":2},{"header":"Updating Collapse Settings","text":"Updating Collapse Settings You can update rowGroupCollapseBehavior dynamically. In this demo, useState manages the property value, which updates via a toggle group to demonstrate the different behaviors. ::demo[Updating Collapse Behavior=\"./demos/client-row-grouping-collapsing-updates\"]","link":"docs/client-source-row-group-collapsing#updating-collapse-settings","depth":2},{"header":"Grouping Function","text":"Grouping Function Provide useClientDataSource with a function that returns an array of path values to group by. A path value is any string or null value. The demo below uses a grouping function to group rows by Job and Education. ::demo[Function Row Grouping=\"./demos/client-row-grouping-fn\"] The group function is straightforward and returns an array of strings. Group functions always receive a leaf row, since leaf rows are the rows that are used to create groupings.","link":"docs/client-source-row-grouping#grouping-function","depth":2},{"header":"Non-Uniform Groups","text":"Non-Uniform Groups If you inspect the GroupFn type, you will notice that it can return null instead of an array. When a group function returns null, the row is not grouped and instead sits at the top level of the view alongside other groups. Furthermore, a grouping function can return group paths of varying lengths. The demo below demonstrates a non-uniform group. Notice that any row with \"Secondary\" education is not grouped, and any row with Marital status equal to “Single” is only grouped by Job. ::demo[Non-Uniform Row Groupings=\"./demos/client-row-grouping-fn-non-uniform\"] :::tip In other data grids, you may have seen non-uniform groups called Tree Data. In LyteNyte Grid, this path-based array of values is simply a non-uniform grouping of rows. LyteNyte Grid provides a tree data source, but it's intended for object data, not array data. :::","link":"docs/client-source-row-grouping#non-uniform-groups","depth":3},{"header":"Grouping Dimensions","text":"Grouping Dimensions The group property accepts an array of dimensions. A dimension is defined by the following type interface: A dimension is any object with a field or id property. All valid LyteNyte Grid columns conform to the Dimension type and can be used as dimensions. The demo below shows how to group rows using dimensions. ::demo[Row Group Dimensions=\"./demos/client-row-grouping-dimension\"] Row grouping using dimensions is easier to manage since you can directly use the columns you pass to the grid. However, dimensions always result in uniform groups.","link":"docs/client-source-row-grouping#grouping-dimensions","depth":2},{"header":"Changing Row Groups","text":"Changing Row Groups Update row groups by changing the group property value. The demo below updates groups using the Pill Manager component. ::demo[Change Row Group=\"./demos/client-row-grouping-dimension-updates\"] :::tip It is generally good practice to hide columns that you group on. In the demo, the code updates the columns based on the current group state before passing them to the grid. This way, when a column is grouped the grid hides the column, and when the column is ungrouped the grid shows the column again. The code snippet is shown below: :::","link":"docs/client-source-row-grouping#changing-row-groups","depth":2},{"header":"Row Group Expansions","text":"Row Group Expansions The row data source maintains the expansion state of row groups in LyteNyte Grid. For the client data source, expansions can be controlled or uncontrolled. To control expansions, pass a rowGroupExpansions value to the useClientDataSource hook. To handle expansion changes, provide an onRowGroupExpansionChange callback on the row source. LyteNyte Grid provides the api.rowGroupToggle method to change the expansion state of a row group. This method calls onRowGroupExpansionChange with the delta changes. The rowGroupDefaultExpansion setting on the client source or grid API updates the expansion state and calls onRowGroupExpansionChange, if provided, on the row data source. Using api.rowGroupToggle, you can create your own group cell renderer to expand and collapse rows. As shown in the demo below. ::demo[Row Group Expansion=\"./demos/client-row-grouping-group-expansions\"]","link":"docs/client-source-row-grouping#row-group-expansions","depth":2},{"header":"Default Expansions","text":"Default Expansions The rowGroupDefaultExpansion property on the client data source controls the default expansion state for any row group that does not have a value in rowGroupExpansions. The rowGroupDefaultExpansion property accepts one of two values: A boolean, where false collapses all rows by default and true expands all rows by default.A positive number, which expands all row groups with a depth value less than or equal to the provided number by default. The demo below demonstrates expanding the first level of row groups by setting the rowGroupDefaultExpansion value to 0. ::demo[Default Row Group Expansion=\"./demos/client-row-grouping-group-default\"]","link":"docs/client-source-row-grouping#default-expansions","depth":3},{"header":"Expand Siblings","text":"Expand Siblings The client row source provides utility methods for querying the structure of the row hierarchy. Using the rowSiblings method, you can build the ability to expand sibling nodes of a group. This method is available on the row source and the grid API. Combine the rowSiblings method with onRowGroupExpansionChange to build this functionality. The demo below demonstrates this functionality. Click the + button instead of the expansion chevron to expand all sibling rows for the current group. ::demo[Sibling Row Group Expansion=\"./demos/client-row-grouping-group-siblings\"]","link":"docs/client-source-row-grouping#expand-siblings","depth":3},{"header":"Sort Functions","text":"Sort Functions A sort function compares two rows (of any kind). The client row data source calls the function you pass to the sort property with two row nodes. The row nodes may be any row type (leaf, group, or aggregation). If you are unfamiliar with row node types, see the Rows Overview guide for more details. The demo below shows basic sorting using a sort function. It covers only the sorting interface's foundational capabilities. The next sections build on this foundation to implement robust sorting in LyteNyte Grid. ::demo[Basic Sort Function=\"./demos/client-row-sorting\"] The demo above covers only the basics of sorting. A more standard sorting interface would typically include additional functionalities such as: Sort indicator on the sorted column, such as an up or down icon.Sorting for any column in the grid, not just Change 24H.A method to switch between ascending and descending sort order.A scalable approach that supports arbitrary sort options, not just predefined buttons. LyteNyte Grid lets you define your own column behavior. Extend the column specification with a sort attribute to represent sorting. In the demo below, you can click any header to sort that column. Defining a sort extension for columns enables this flexibility. ::demo[Extended Function Sort=\"./demos/client-row-sorting-fn-extension\"] This demo shows function-based sorting. LyteNyte Grid supports custom sort models, allowing you to implement the sorting behavior your application requires. Function-based sorting is flexible, but it has drawbacks: For basic sorts, writing a custom sort function is tedious.Function sorting doesn't declaratively represent multi-way sorting; it requires implementing multi-way sorts within the function.The sort function must handle sort direction. LyteNyte Grid's client row source supports dimension sorts, which are simpler than function sorts without sacrificing flexibility","link":"docs/client-source-row-sorting#sort-functions","depth":2},{"header":"Dimension Sorts","text":"Dimension Sorts A dimension sort is an object representation of an individual sort. The interface is shown below: Notice that the dim property can be either a Dimension or a SortFn. This means it is possible to represent a SortFn as a dimension sort: The dim field may also be a Dimension, which is any value with an id or field property, as shown in the interface below. In particular, columns passed to the grid may also be used as dimensions. LyteNyte Grid's client source can compute column fields, which makes it easy to use columns as dimensions. In the demo below, the columns have been extended with custom sort attributes. Instead of using a function to sort the row data, the demo uses the column directly as a dimension. ::demo[Column Dimension Sort=\"./demos/client-row-sorting-dimensions\"]","link":"docs/client-source-row-sorting#dimension-sorts","depth":2},{"header":"Multi-Way Sorting","text":"Multi-Way Sorting Dimension sorts make multi-way sorting straightforward. A multi-way sort defines an array of comparators. The grid applies the first comparator; if it returns 0, the grid applies the next, continuing until a comparator returns a non-zero result or the list ends. The demo below shows multi-way sorting using dimension sorts. To sort on more than one column, hold the Control/Command key and click a column header. ::demo[Column Multi-way Sort=\"./demos/client-row-sorting-multi-way\"] The demo's multi-way sort implementation replaces all existing sorts when you click a header without pressing Control/Command. This behavior is not a strict rule. Instead, it is part of the intended implementation for this demo. LyteNyte Grid's dimension sorts and API extensions let you define the interaction you want. Choose the interaction that fits your application.","link":"docs/client-source-row-sorting#multi-way-sorting","depth":3},{"header":"Sorting Group Rows","text":"Sorting Group Rows You can apply the same sorts used for ordering leaf rows to order group rows. Group rows do not require special handling; simply sort by the target dimension. The demo below illustrates group row sorting. See the Client Row Grouping guide for more on group functionality. ::demo[Sorting Group Rows=\"./demos/client-row-sorting-groups\"] This demo uses a different approach than the other demos in this guide to further emphasize the flexibility of defining your own sort model. Instead of extending the columns with a sort value, the demo maintains a separate sort state: :::tip LyteNyte Grid automatically creates a row group column when group rows are present in the grid. Because this column is not part of the columns you pass to the grid, the correct id for the dimension may not be obvious. LyteNyte Grid's group column uses the id \"__ln_group__\". The client row source treats this ID in a special way and will sort rows based on the group's key. This means you can sort the group column with: :::","link":"docs/client-source-row-sorting#sorting-group-rows","depth":2},{"header":"Pinning Rows Top","text":"Pinning Rows Top To pin rows to the top of the grid, set the topData property on the client row data source. The demo below pins two rows to the top of the viewport. Notice that these rows remain visible as you scroll. ::demo[Top Pinned Rows=\"./demos/client-top-rows\"]","link":"docs/client-source-row-pinning#pinning-rows-top","depth":2},{"header":"Pinning Rows Bottom","text":"Pinning Rows Bottom To pin rows to the bottom of the grid, set the botData property on the client row data source. The demo below pins two rows to the bottom of the viewport. These rows remain fixed to the bottom of the viewport even as you scroll. ::demo[Bottom Pinned Rows=\"./demos/client-bot-rows\"]","link":"docs/client-source-row-pinning#pinning-rows-bottom","depth":2},{"header":"Pinning Rows Top and Bottom","text":"Pinning Rows Top and Bottom You can pin rows to both the top and bottom of the viewport by setting both topData and botData at the same time. Since pinned rows always remain visible, you must ensure the grid's viewport has enough height to display them. ::demo[Top & Bottom Pinned Rows=\"./demos/client-top-and-bot-rows\"]","link":"docs/client-source-row-pinning#pinning-rows-top-and-bottom","depth":2},{"header":"Pinned Row Considerations","text":"Pinned Row Considerations The client data source treats pinned rows as separate from the row data provided via the data property. In particular: Pinned rows will never be filtered out.Sorting rows will not sort pinned rows.Pinned rows cannot be used for groups.","link":"docs/client-source-row-pinning#pinned-row-considerations","depth":2},{"header":"Autosizing Cells","text":"Autosizing Cells Use the api.autosizeColumns method to autosize all columns or a selected subset. The demo below shows this behavior. Clicking Autosize Cells triggers the autosizing logic. ::demo[Cell Autosize=\"./demos/column-autosizing\"] The Autosize Cells button calls api.autosizeColumns to compute and apply new widths. When you call this method without arguments, the grid autosizes every column. Autosizing is a one off operation, if cell data changes later, the grid does not autosize again. Call api.autosizeColumns whenever updated content requires resizing.","link":"docs/column-autosizing#autosizing-cells","depth":2},{"header":"Autosize Header Text","text":"Autosize Header Text The api.autosizeColumns method accepts an option that includes a column's header content in the calculation. You can customize how header width is measured using autosizeHeaderFn. This is useful when header text is longer than any cell in the column. In the demo below, the Crypto Currency Symbol, Ticker, and Name column has a long header, so autosizing expands the column to fit the header. ::demo[Autosize Header Text=\"./demos/column-autosizing-header\"] The Autosize Cells Including Header button passes the includeHeader flag:","link":"docs/column-autosizing#autosize-header-text","depth":3},{"header":"Autosize Calculators","text":"Autosize Calculators Columns can define their own autosize calculation functions. When you call api.autosizeColumns, the grid uses these functions to compute each width. The example below shows the autosize functions used in this guide's demos: These autosize functions mirror the structure of the cell renderer. They account for the logo icon, ticker text, symbol name, and the spacing added by CSS. The autosizeCellFn combines these measurements into a final width. The autosizeHeaderFn and autosizeCellFn functions above use measureText, a utility exported from the LyteNyte Grid package. The measureText function uses an offscreen canvas to approximate text width based on the provided element, in this case the viewport.","link":"docs/column-autosizing#autosize-calculators","depth":3},{"header":"Autosize Dry Run","text":"Autosize Dry Run The autosize method returns an AutosizeResult object mapping column ids to their computed widths. It also supports a dry-run mode that calculates widths without applying them:","link":"docs/column-autosizing#autosize-dry-run","depth":3},{"header":"Autosizing Individual Columns","text":"Autosizing Individual Columns You can autosize only specific columns by passing their identifiers to api.autosizeColumns. The demo below shows Autosize Symbol and Autosize Network, which resize only those columns. ::demo[Autosize Individual Columns=\"./demos/column-autosizing-individual\"] Specify the columns to autosize using any supported reference:","link":"docs/column-autosizing#autosizing-individual-columns","depth":2},{"header":"Double Click To Resize","text":"Double Click To Resize The columnDoubleClickToAutosize grid property controls whether LyteNyte Grid autosizes a column when you double-click the column's right edge. Try double-clicking the header edges in the demo below. ::demo[Double Click To Autosize=\"./demos/column-autosizing-double\"] For double-click autosizing to work, the column must be resizable. Set the resizable property on the column's specification. The example below enables double-click resizing for all columns:","link":"docs/column-autosizing#double-click-to-resize","depth":2},{"header":"Virtualization Considerations","text":"Virtualization Considerations LyteNyte Grid uses row virtualization for performance. Autosizing considers only visible rows. If the viewport has not initialized, the grid samples about 50 rows for calculations. If a column has a known set of possible values, consider writing an autosize function that returns the maximum width required for those values, independent of viewport visibility. To learn more about LyteNyte Grid's virtualization features and configuration options, see the Row & Column Virtualization guide. :::info LyteNyte Grid autosizes every column by default, even when virtualization keeps a column out of the DOM. If your grid defines many columns, consider autosizing only the visible ones to avoid unnecessary work. :::","link":"docs/column-autosizing#virtualization-considerations","depth":2},{"header":"Overriding Defaults","text":"Overriding Defaults Every column in LyteNyte Grid extends a default column specification. The grid uses this specification to fill in any values omitted from your column definitions. For example: The name column uses the default width of 150, while the email column uses its specified width of 200. Every property on the column interface has a default value. Refer to the Column API reference for details. To override these defaults, provide a columnBase object. LyteNyte Grid merges this object with the built-in defaults, overriding any specified properties. For example, in the code below the default column width becomes 100. You can override any default property on the column specification except those properties that have been explicitly excluded, such as field or pin. A common pattern is to set a default cell renderer or header renderer. The demo below creates a heatmap of temperatures using a shared default cell renderer: ::demo[Monthly Temperatures Heatmap=\"./demos/column-base-temperature\"] The demo overrides width, widthMin, and widthFlex. Since those defaults are tailored for monthly temperature columns, the year column must specify its own values:","link":"docs/column-base#overriding-defaults","depth":2},{"header":"Exclusions From the Base","text":"Exclusions From the Base The columnBase property cannot override every column option. In particular, pin and field must be set on a per column basis and cannot be defined through the base. These properties apply only to specific columns, not all columns, so this limitation is intentional.","link":"docs/column-base#exclusions-from-the-base","depth":3},{"header":"String Fields","text":"String Fields When field is a string, the grid uses it as a key to read the value from object-based row data. Use string fields when each row is a standard JavaScript object. This is demonstrated in the example below: ::demo[String Field=\"./demos/column-field-string\"] In the example, each column specifies its field directly:","link":"docs/column-field#string-fields","depth":2},{"header":"Number Fields","text":"Number Fields When field is a number, row data must be an array. The number acts as the index used to retrieve the value. In the demo below, each row is an array, so each column uses a numeric index. ::demo[Number Index Fields=\"./demos/column-field-number-index\"] The column definitions specify numeric indices. These indices do not need to be sequential: Number fields are efficient. In JavaScript, array indexing usually performs better than key-based lookups, and array-structured row data is often more compact than object-based data. This makes number fields a strong choice for performance-sensitive grids.","link":"docs/column-field#number-fields","depth":2},{"header":"Function Fields","text":"Function Fields A function field offers the most flexible way to compute a column's value. You provide a function, and LyteNyte Grid calls it for each row to compute the cell value. Function fields are commonly mixed with other field types rather than being used for every column. LyteNyte Grid passes a single argument to the function. This argument contains either aggregated group data or leaf-row data, depending on the row being processed. See the field data param for the exact type. Function fields are ideal for derived or calculated values. In the demo below, the GBP Price column computes a value from the base USD Price column. ::demo[Function Fields=\"./demos/column-field-function\"]","link":"docs/column-field#function-fields","depth":2},{"header":"Path Fields","text":"Path Fields Set the field property to an object of the form { kind: \"path\"; path: string } to retrieve a nested value from the row data. The path syntax matches Lodash's get function. Examples: The demo below shows path fields in use. Each row exposes temperatures under a temps property keyed by month, so fields such as { kind: \"path\", path: \"temps.Jan\" } read the appropriate value. ::demo[Path Fields=\"./demos/column-field-path\"]","link":"docs/column-field#path-fields","depth":2},{"header":"Floating Component Renderer","text":"Floating Component Renderer When the floating row is enabled, LyteNyte Grid uses each column's floatingCellRenderer to decide what to render in the floating cell. The demo below shows a simple renderer that provides a basic text filter input. ::demo[Column Floating Cell=\"./demos/column-floating\"]","link":"docs/column-floating-header#floating-component-renderer","depth":2},{"header":"Floating Row Visibility Toggling","text":"Floating Row Visibility Toggling You can show or hide the floating row by toggling the floatingRowEnabled state. The demo below includes a switch that updates this state: ::demo[Floating Cell Visibility=\"./demos/column-floating-visibility\"] The toggle simply flips the floatingRowEnabled value:","link":"docs/column-floating-header#floating-row-visibility-toggling","depth":2},{"header":"Creating a Column Group","text":"Creating a Column Group Create column groups in LyteNyte Grid by setting the groupPath property on each column. The grid builds a hierarchy from the groupPath values across all columns. The demo below shows a basic column group setup. ::demo[Column Groups=\"./demos/column-groups\"] Columns stay as a flat array, even when grouped. LyteNyte Grid creates the hierarchy by joining each column's group path. Columns with the same group path belong to the same group. In the example below, the Symbol, Network, and Exchange columns all belong to the Market Info group. Columns do not need to belong to a group. If a column has no group path, its header cell spans the full height of the header.","link":"docs/column-groups#creating-a-column-group","depth":2},{"header":"Split Column Groups","text":"Split Column Groups LyteNyte Grid uses the groupPath property to identify column groups. A group appears split when columns from different groups are placed between its members. Although the group remains a single logical entity, it renders as multiple visual segments. When you move separated columns next to each other, the grid automatically merges the segments. Pinned columns always appear visually separate from non-pinned columns, even when they share a group. The demo below shows this behavior. The Market Info and Performance groups are split visually but remain single logical groups. ::demo[Split Column Groups=\"./demos/column-groups-split\"]","link":"docs/column-groups#split-column-groups","depth":3},{"header":"Multiple Column Group Levels","text":"Multiple Column Group Levels The groupPath property is an array of strings. Each string represents a grouping level. Adding more entries creates deeper nesting. The demo below illustrates multiple group levels: ::demo[Multiple Group Levels=\"./demos/column-groups-multiple-levels\"] Each grouping level creates a new header row. The longest groupPath in the grid determines the number of header rows.","link":"docs/column-groups#multiple-column-group-levels","depth":2},{"header":"Column Groups & Pinned Columns","text":"Column Groups & Pinned Columns Pinned columns can still belong to a group. Pinned columns create a split boundary, so the grid treats them as visually separate even if they are adjacent to other group members. The example below shows this: ::demo[Pinned Column Groups=\"./demos/column-groups-expansions-pinned\"] Set the pin property to \"start\" or \"end\" to pin a column to the start or end of the viewport. For details, see the Column Pinning guide.","link":"docs/column-groups#column-groups--pinned-columns","depth":2},{"header":"Column Group Expansions","text":"Column Group Expansions Column groups become collapsible when you use the groupVisibility property, which controls when each column is visible: \"open\": Visible only when the group is expanded.\"closed\": Visible only when the group is collapsed.\"always\": Always visible. A group becomes collapsible when at least one column is visible in the collapsed state and at least one is visible in the expanded state. In the demo below, the Market Info and Performance groups are collapsible. The Market Info group starts collapsed. Click the chevron to expand it. ::demo[Column Group Expansion=\"./demos/column-groups-expansions\"] The demo uses a custom header renderer to manage the column group expansion state. A simplified implementation is shown below. Notice the call to api.columnToggleGroup. You can use this method to programmatically toggle the column group expansion state.","link":"docs/column-groups#column-group-expansions","depth":2},{"header":"Default Group Expansion","text":"Default Group Expansion Collapsible groups are expanded by default. Use the columnGroupDefaultExpansion property to change this. Setting it to false will collapse column groups by default: ::demo[Default Expansion State=\"./demos/column-groups-default-expansions\"] You can also provide explicit initial expansion states. To do this, you must know how LyteNyte Grid generates group IDs. Group IDs come from joining the groupPath array with a delimiter. The default delimiter is -->, but you can change it using columnGroupJoinDelimiter. For example: With this information, you can set expansion states directly:","link":"docs/column-groups#default-group-expansion","depth":3},{"header":"Sticky Group Labels","text":"Sticky Group Labels Some groups contain many columns, and horizontal scrolling can hide the group label. LyteNyte Grid allows you to style header cells so the label remains visible. The demo below shows this behavior. Try scrolling horizontally and observe that the Market Info label remains in view. ::demo[Sticky Group Labels=\"./demos/column-groups-sticky\"] The implementation is simple. The important part is the styles override. Grid.HeaderGroupCell uses position: relative and overflow: hidden by default. In this case, overriding those defaults is appropriate. Since pinned columns can be pinned to the start, we use the --ln-start-offset variable, which equals the total width in pixels of the columns pinned to the start. LyteNyte Grid sets --ln-start-offset on the grid viewport.","link":"docs/column-groups#sticky-group-labels","depth":2},{"header":"Column Groups & Header Spanning","text":"Column Groups & Header Spanning When column groups are enabled, some column headers may span across multiple group rows, increasing the cell's header height since it must bridge the missing group rows. This occurs when: A column does not belong to any groupThe columns group path is shallower than the deepest group level In the demo below, Price and Product belong to the Inventory group. All other columns are ungrouped, so their header cells span every group row and reach the leaf row, giving them the full header height. A header cell's height is the leaf header (base) height plus the combined height of the group rows it spans. If all group rows share height G, then header height is determined as follows: ::demo[Column Header Group Span=\"./demos/column-header-height-spans\"]","link":"docs/column-header-height#column-groups--header-spanning","depth":2},{"header":"Column Group Header Height","text":"Column Group Header Height Set the height of group header rows using the headerGroupHeight property on the grid. All column groups share the same group-header height. ::demo[Column Group Header Height=\"./demos/column-header-group-height\"]","link":"docs/column-header-height#column-group-header-height","depth":2},{"header":"Floating Row Height","text":"Floating Row Height The floating row appears directly under the column header and is useful for supplemental UI, such as floating filters. Set its height using the floatingRowHeight property. The demo below shows a simple example that adds text-matching filters for string columns. For more details, see the Column Floating Header guide. ::demo[Floating Row Height=\"./demos/column-header-floating-row-height\"]","link":"docs/column-header-height#floating-row-height","depth":2},{"header":"Unique Column IDs","text":"Unique Column IDs LyteNyte Grid uses the id of a column to uniquely identify and track the column. The id is used for retrieval, detecting movement, and computing cell values. Column IDs are immutable. If you change a column's id value, LyteNyte Grid treats the column as a new column, even if the rest of the object is unchanged. A column id can be any string that fits your data model. Many developers set the id to a key on the row data. This is convenient since LyteNyte Grid also uses the id as a fallback when a column does not define a field. See the Column Field guide for details.","link":"docs/column-header-name#unique-column-ids","depth":2},{"header":"Column Display Name","text":"Column Display Name By default, LyteNyte Grid uses the column's id as its display name. The display name appears in the column header and in components that manage columns, such as the Column Manager. If the id is not suitable for display, set the name property to a human-readable string. In the example below, several columns specify a name to improve their header text: ::demo[Human Readable Names=\"./demos/column-overview-demo\"] In this example, purchaseDate, paymentMethod, and id use name to provide readable labels, while the remaining IDs are already clear. Developers often set name when the id is written in snake case or camel case.","link":"docs/column-header-name#column-display-name","depth":2},{"header":"Updating the Display Name","text":"Updating the Display Name A column's id is immutable, but its name can be updated. To change the display name, update the column specification. LyteNyte Grid provides the columnUpdate API for updating individual columns. For example, you may want users to rename columns. In the demo below, users can double-click a header to edit the display name. Pressing Enter or clicking away saves the new value. ::demo[Renaming Column Headers=\"./demos/column-header-name-update\"] To support editing header names, this example uses a custom header cell and extends the grid API to enable it. For more guidance on extending the grid API, see the API Extension guide.","link":"docs/column-header-name#updating-the-display-name","depth":2},{"header":"Header Renderers","text":"Header Renderers Creating a header renderer involves the following steps: Define a custom React component function that accepts an object of type Grid.T.HeaderParams as its props.Set the value of the headerRenderer property for each column that should use this renderer. The demo below shows this in action. Each column has a custom headerRenderer that adds an icon based on the column ID. ::demo[Header Renderers=\"./demos/column-header-renderer\"] The header renderer in the demo is a normal React component. LyteNyte Grid renders it within the same React tree as the rest of your application. This means the renderer can access React context and use any React hooks.","link":"docs/column-header-renderer#header-renderers","depth":2},{"header":"Move By Dragging","text":"Move By Dragging LyteNyte Grid supports column reordering through the movable property on each column definition. Set movable to true to allow users to drag a column header to a new position. Click and hold to drag a header in the demo below: ::demo[Move By Dragging=\"./demos/column-moving\"]","link":"docs/column-moving#move-by-dragging","depth":2},{"header":"Pinned Columns & Drag Reordering","text":"Pinned Columns & Drag Reordering When the grid has both pinned and scrollable columns, drag-and-drop reordering updates the column's pin state depending on where you drop it. Moving an unpinned column over a pinned column causes the moved column to become pinned.Moving a pinned column over an unpinned one unpins the moved column. Crossing the pinned or scrollable boundary when moving columns changes the pin state. However, reordering columns within the same section without crossing the boundary leaves it unchanged. The demo below shows this boundary-based behavior. ::demo[Moving Pinned Columns=\"./demos/column-moving-pinned\"]","link":"docs/column-moving#pinned-columns--drag-reordering","depth":3},{"header":"Dragging Column Groups","text":"Dragging Column Groups Columns in the same group can be dragged together. All columns in the group must be movable for group dragging to work. In the demo below, dragging the Inventory group moves both the Product and Price columns: ::demo[Moving Column Groups=\"./demos/column-moving-group\"] Moving grouped columns can change the group hierarchy: If you drag a column out of its group, LyteNyte splits the group.If two columns belong to the same group but are separated in the header hierarchy, dragging them next to each other merges the header hierarchy.Reordering columns within the same group behaves the same as reordering ungrouped columns.If you move an ungrouped column between columns in a group, LyteNyte splits the group. See the Column Groups guide for details on when groups split and how LyteNyte Grid handles column groups.","link":"docs/column-moving#dragging-column-groups","depth":2},{"header":"Drag Placeholder","text":"Drag Placeholder When column dragging begins, the browser takes an image snapshot of the column being dragged. This is native browser behavior. LyteNyte Grid lets you customize the drag placeholder. The grid exposes two properties: columnMoveDragPlaceholder: Determines the placeholder used when dragging column headers.columnGroupMoveDragPlaceholder: Determines the placeholder used when dragging column group headers. The placeholder can be one of the following: String: A CSS selector that targets an element to snapshot as the drag image.Query Object: An object that includes a CSS selector string and an (x,y) offset that positions the snapshot image.Component Placeholder: A React component that renders the placeholder. :::note When you use a Component Placeholder, you disable the browser's native drag-image snapshot. LyteNyte Grid provides the client coordinates for the drag event, and your code must render the placeholder correctly at those coordinates. LyteNyte Grid mounts the placeholder within the header component in the React tree, so you must use a React portal to render the placeholder into the target DOM container. ::: In the demo below, a custom React component renders the drag placeholder for column headers and column group headers. ::demo[Column Placeholder Dragging=\"./demos/column-moving-placeholder\"] Using a React component instead of a query selector or default drag behavior has differences to note, even if not obvious from the drag examples. Native Placeholder (Query Selector) Uses the browser's drag-and-drop API.Snapshots the dragged element when the drag starts.Uses a static image for the entire drag that cannot be updated after the snapshot is taken. React Component Placeholder Re-renders every time the cursor moves.You can update the placeholder's visual output during the drag.Runs significantly slower than native placeholder approach Use a React component placeholder only when you need dynamic visuals. Otherwise, prefer query selectors and elements for drag placeholders.","link":"docs/column-moving#drag-placeholder","depth":2},{"header":"Column Dropped Outside Viewport","text":"Column Dropped Outside Viewport LyteNyte Grid detects when a moved column or column group is dropped outside the grid. When this happens, the grid calls the onColumnMoveOutside handler (if provided). You can use this handler to determine whether a column move ended outside the grid. If you want to hide a column when it is dropped outside the grid, attach a callback to onColumnMoveOutside to hide the moved columns, as demonstrated in the demo below. ::demo[Outside Drag-to-Hide=\"./demos/column-moving-outside\"] :::tip LyteNyte Grid supports hiding columns when users drag them outside the grid, but we do not recommend implementing this behavior. Our experience shows that users find this behavior unintuitive and easy to get wrong. A better approach is to use a dedicated component that manages column visibility, such as our Pill Manager component or our Column Manager component. :::","link":"docs/column-moving#column-dropped-outside-viewport","depth":2},{"header":"Programmatic Column Reordering","text":"Programmatic Column Reordering Use api.columnMove to reorder columns programmatically. Column order is determined by the sequence of columns in the grid state, and api.columnMove updates columns property accordingly. For example, the code below swaps the ID and Product columns: For more details, see the Grid API reference.","link":"docs/column-moving#programmatic-column-reordering","depth":2},{"header":"Pinning Columns to Start","text":"Pinning Columns to Start Set a column's pin property to \"start\" to pin it to the beginning of the grid's viewport. In LTR mode, start-pinned columns appear on the left; in RTL mode, they appear on the right. ::demo[Column Pinning Start=\"./demos/column-pinning-start\"] In the demo, the ID and Product columns are pinned to the start of the viewport:","link":"docs/column-pinning#pinning-columns-to-start","depth":2},{"header":"Pinning Columns to End","text":"Pinning Columns to End Set a column's pin property to \"end\" to pin it to the end of the grid's viewport. In LTR mode, end-pinned columns appear on the right; in RTL mode, they appear on the left. ::demo[Column Pinning End=\"./demos/column-pinning-end\"] In the demo, the ID and Product columns are pinned to the end of the viewport:","link":"docs/column-pinning#pinning-columns-to-end","depth":2},{"header":"Pinning Columns Start & End","text":"Pinning Columns Start & End LyteNyte Grid supports pinning columns to both the start and end. The demo below shows this behavior. Pin columns on both sides by assigning some columns pin: \"start\" and others pin: \"end\". ::demo[Column Pinning Start & End=\"./demos/column-pinning-both\"] In the demo, the ID and Product columns are pinned to the start and end of the viewport respectively:","link":"docs/column-pinning#pinning-columns-start--end","depth":2},{"header":"Column Pinning Considerations","text":"Column Pinning Considerations When using both start-pinned and end-pinned columns, ensure enough space remains for unpinned columns to scroll between them. LyteNyte Grid does not prevent pinned regions from overlapping. If they overlap, columns pinned to \"end\" appear above those pinned to \"start\".","link":"docs/column-pinning#column-pinning-considerations","depth":2},{"header":"Resizing Columns","text":"Resizing Columns Columns in LyteNyte Grid always have a fixed width. To resize a column, update the column's width property. Users can drag the edge of a column header to change the column's width. LyteNyte Grid supports this interaction out of the box. Enable it by setting the resizable property to true on the column specification. You can make all columns resizable by setting resizable on the base column. When resizable is true, the grid renders a resize handle on the header's edge. Users can drag this handle to change the width of a column. The demo below enables the drag handle. Try placing your cursor on the right edge of a column header and dragging once the hover indicator appears. ::demo[Resizing Columns=\"./demos/column-resizing-drag\"] :::warn Dragging to resize does not work on touch devices because touch dragging scrolls the viewport. The developer should provide an alternative resize action, such as a “size to fit” button for accessibility purposes. :::","link":"docs/column-resizing#resizing-columns","depth":2},{"header":"Customizing the Resize Handle","text":"Customizing the Resize Handle LyteNyte Grid renders the resize handle as an invisible div. You can style it using CSS or by passing properties to the Grid.HeaderCell component. Two properties customize the resize handle: resizeStyle: Inline styles applied to the handle.resizeClassName: CSS class names applied to the handle. In most cases, resizeClassName is enough to customize the handle's appearance. If you use a built-in grid theme, the handle already has styling. The default themes use the following CSS, which you can reference when implementing your own customizations:","link":"docs/column-resizing#customizing-the-resize-handle","depth":3},{"header":"Resizing Programmatically","text":"Resizing Programmatically For one-off resizes or custom resize workflows, call the grid API's columnResize method. It accepts a JavaScript object mapping column IDs to widths. The columnResize method is a convenience wrapper around columnUpdate. The following code produces the same result:","link":"docs/column-resizing#resizing-programmatically","depth":3},{"header":"Min & Max Column Width","text":"Min & Max Column Width Use the widthMin and widthMax properties to clamp a column's width. By default, columns have a minimum width of 80px and a maximum width of 1000px. The demo below clamps column widths between 100px and 300px. When a user resizes a column past these bounds, LyteNyte Grid will use a clamped width value for layout calculations. ::demo[Column Width Bounds=\"./demos/column-resizing-minmax\"] The column widthMin and widthMax properties define visual layout bounds that LyteNyte Grid enforces. LyteNyte Grid does not clamp the column's width value to those limits, so your code can set width outside widthMin/widthMax. If width is out of bounds and you later change widthMin or widthMax, LyteNyte Grid immediately resizes the column to fit the updated limits.","link":"docs/column-resizing#min--max-column-width","depth":3},{"header":"Column Width Flex","text":"Column Width Flex Every column has a specified width or a default width. When the total column width is smaller than the viewport, you can allocate the remaining space using widthFlex. This property defines how much of the free space a column should take, similar to the CSS flex property. ::demo[Full Width Viewport Filling=\"./demos/column-resizing-width-flex\"] :::info widthFlex is most noticeable on wide viewports. On small screens, you may not see columns expand. View this demo on a desktop-sized display to see the effect. :::","link":"docs/column-resizing#column-width-flex","depth":2},{"header":"Size To Fit","text":"Size To Fit The columnSizeToFit property sizes columns so they fit within the viewport. When total column width exceeds the viewport, the grid shrinks column widths. When total width is smaller, the grid expands them. In the demo below, you can toggle the columnSizeToFit property to observe how it changes the widths of the column. The smaller the size of your display the more pronounced the effect will be. ::demo[Size-to-Fit Columns=\"./demos/column-resizing-size-to-fit\"] Use the columnSizeToFit property sparingly. Columns can appear compressed on narrow viewports, and cell content may truncate. Only enable this feature when all columns must be visible.","link":"docs/column-resizing#size-to-fit","depth":2},{"header":"Measure Text Function","text":"Measure Text Function LyteNyte Grid exports a measureText function that approximates the pixel width of a string. This is useful when adjusting cell content based on available space. Measuring the width of a given cell's text, allows for some interesting use cases. For example, applications like Excel show # symbols when a number overflows the cell. The demo below demonstrates the pattern by toggling this effect on and off. String Truncation: The Symbol and Exchange columns, which contain strings, are truncated.Number Hashing: The Performance and Volatility columns, which contain numbers, are hashed. ::demo[Truncating and Hashing=\"./demos/column-resizing-measure-text\"] In the demo, the HashedValue component uses measureText inside a useMemo call. Measuring text is expensive, so caching the result avoids running the calculation on every render. The measureText function has many uses. LyteNyte Grid relies on it internally for autosizing and other text-based measurements. Remember that the function returns an approximation, not an exact value. Differences in browser text rendering prevent pixel-perfect consistency.","link":"docs/column-resizing#measure-text-function","depth":2},{"header":"Uniform Column Spans","text":"Uniform Column Spans When colSpan is a number, every cell in the column spans the same number of adjacent columns for all rows. The grid skips rendering any cells covered by the span. The demo below shows the USD Price column spanning over the adjacent GBP Price column. ::demo[Uniform Column Span=\"./demos/column-spanning-number\"] Uniform spans are uncommon and mainly appear in cases where the visual layout differs from the logical column structure. Hence, these types of column spans are used sparingly.","link":"docs/column-spanning#uniform-column-spans","depth":2},{"header":"Function Column Spans","text":"Function Column Spans You can set colSpan to a function that returns the span for each row. This allows dynamic spans based on row data. The demo below merges cells for rows whose temperature values differ by less than 3. ::demo[Function Column Span=\"./demos/column-spanning-fn\"] The colSpan function below shows how flexible span logic can be:","link":"docs/column-spanning#function-column-spans","depth":2},{"header":"Hiding Columns","text":"Hiding Columns Each column in LyteNyte Grid has a hide property in its specification. This property defaults to false. When set to true, the grid does not render the column in the viewport. The grid still includes the column internally, and queries against grid state will continue to return it. In the demo below, the Network and Exchange columns start hidden. Clicking their pills toggles each column's visibility. When a hidden column becomes visible, the grid inserts it based on its position in the column state. ::demo[Hiding Columns=\"./demos/column-visibility\"] The demo uses the Pill Manager provided by LyteNyte Grid. Clicking a pill toggles the visibility of its corresponding column. Since columns are updated at runtime, their column definitions are stored in React state. The Pill Manager is a versatile component that manages column visibility, ordering, grouping, and additional functions. See the Pill Manager guide for more details. :::warn Avoid updating a column's visibility too frequently. Each update triggers a layout recalculation and DOM update, which can be expensive when performed many times per second. :::","link":"docs/column-visibility#hiding-columns","depth":2},{"header":"Column Group Visibility","text":"Column Group Visibility A column group is a developer-defined collection of related columns. A group may be collapsed, causing certain columns to appear only when the group is open. When the grid collapses a group, it hides the affected columns visually. However, this behavior differs from setting hide on a column specification. The grid still considers these columns logically visible. In other words, their visibility state remains true even though the collapsed group hides them in the UI. The example below demonstrates this behavior. The Market Info group is collapsed, which hides the Network and Exchange columns. The Pill Manager still shows them as visible because the grid treats them as visible in state, even though the collapsed group hides them visually. ::demo[Column Group Visibility=\"./demos/column-visibility_groups\"] :::note Hiding the Symbol column or both the Exchange and Network columns makes the column group no longer collapsible. A column group can be collapsed only if at least one column is visible when the group is open and at least one column is visible when it is closed. For more on column groups, see our Column Groups guide. :::","link":"docs/column-visibility#column-group-visibility","depth":2},{"header":"Columns View Specification","text":"Columns View Specification A column in LyteNyte Grid defines the specification (or type interface) the grid follows to configure how cells and headers are displayed. LyteNyte Grid accepts an array of columns, which developers provide to the grid via the columns property. For example, defining a set of columns can be done as follows: Notice from the definitions above: Each column has an id property. This is the only required property for columns provided to LyteNyte Grid.The width property determines the horizontal space the column's cells occupy in the viewport.The widthMin sets the minimum allowable width for the column.The name property provides a display name for components that use the column specification.The cellRenderer property accepts a React component for rendering the cell's content. The example specification contains only a few of the available properties for a column. The remaining guides in this section cover all configuration possibilities, but you can refer to the Column API reference for details on every configuration option a column accepts. Think of columns declaratively. As the developer, you provide LyteNyte Grid with the column specification. LyteNyte Grid follows the specification to create the correct view. Hence, updating the columns in the grid updates the current view. LyteNyte Grid does not maintain a separate column state from the column specification. The columns property is the source of truth for the grid. This source of truth includes implicit state, such as column ordering, which the grid infers from the order of columns in the array.","link":"docs/columns#columns-view-specification","depth":2},{"header":"Complete Working Example","text":"Complete Working Example The example below shows the columns defined earlier in use. Use this example as a lightweight introduction to what is possible with columns and how even a relatively small amount of configuration creates rich data experiences. ::demo[Columns in Action=\"./demos/column-overview-demo\"]","link":"docs/columns#complete-working-example","depth":2},{"header":"Enabling Marker Column","text":"Enabling Marker Column To enable the marker column, add a columnMarker definition to the grid and set its on property to true. The demo below shows the marker column displaying row indices. The displayed index starts at 1, while actual row indices in LyteNyte Grid start at 0. ::demo[Row Index Marker Column=\"./demos/column-marker\"] The marker column differs from standard columns and has important restrictions: It is always pinned to the start of the viewport.It cannot be moved or reordered.It is not part of the columns state but does affect column display order. When enabled, the marker column will always appear first, and display index 0 always refers to the marker column.","link":"docs/marker-column#enabling-marker-column","depth":2},{"header":"Selection Marker Column","text":"Selection Marker Column A common use case for the marker column is row selection. The marker column can render a checkbox for each row. Because this is so common, LyteNyte Grid provides the api.rowHandleSelect method to simplify selection logic, including shift-select behavior. ::demo[Marker Column Selection=\"./demos/column-marker-selection\"] The demo uses api.rowHandleSelect to manage checkbox interactions:","link":"docs/marker-column#selection-marker-column","depth":2},{"header":"Column Manager Implementation","text":"Column Manager Implementation The demo below shows the Column Manager. Click a checkbox to toggle a column's visibility, or drag a column within the manager to reorder it. ::demo[Column Manager=\"./demos/column-manager\"] As shown in the demo code, configuring the Column Manager is straightforward. It abstracts the complexity of column state management into a single, easy-to-use component. :::note For a less opinionated approach, use the Pill Manager or Tree View. Both are headless and allow flexible configuration. :::","link":"docs/component-column-manager#column-manager-implementation","depth":2},{"header":"Grid Context Menu","text":"Grid Context Menu Use the grid's events property to handle contextmenu on the grid region that should open the context menu. In the demo below, right-click any cell to trigger the context menu and perform an action. ::demo[Context Menu=\"./demos/context-menu\"] The event handler code is shown below. The demo registers contextmenu handlers on both header and row cells. Since the browser natively opens its own menu on right-click, call preventDefault to suppress it. The handlers use LyteNyte Grid's virtualFromXY utility to create a virtual element and assign it to the anchor state. The menu uses the virtual element as its positioning anchor, eliminating the need for a menu trigger.","link":"docs/component-context-menu#grid-context-menu","depth":2},{"header":"Dialog Anatomy","text":"Dialog Anatomy LyteNyte Grid's dialog is a headless component composed of several parts that you must assemble before use. The example below provides a brief overview of these parts.","link":"docs/component-dialog#dialog-anatomy","depth":2},{"header":"Dialog Configuration","text":"Dialog Configuration The demo below demonstrates a basic dialog. It contains the minimum structure required for a fully accessible dialog, which you can expand upon to meet your application's needs. ::demo[Basic Dialog Implementation=\"./demos/dialog-demo\"]","link":"docs/component-dialog#dialog-configuration","depth":2},{"header":"Dialog Properties","text":"Dialog Properties Dialog elements accept standard HTML props. Some parts also support additional properties, which are documented below.","link":"docs/component-dialog#dialog-properties","depth":2},{"header":"Viewport Overlays","text":"Viewport Overlays Set slotViewportOverlay to a React element to render an overlay over the entire grid. Use this for overlays that block pointer interaction, such as loading screens. The demo below shows a loading overlay. Toggle the switch to show or hide it. ::demo[Display Loading Overlay=\"./demos/overlays-viewport\"] The loading overlay code appears below. The overlay uses absolute positioning and fills the viewport to sit above all grid content. The grid header and rows are positioned elements with a maximum z-index of 11. Since the overlay renders inside the viewport div element, set its z-index to 12 or higher to ensure it sits on top.","link":"docs/component-grid-overlays#viewport-overlays","depth":2},{"header":"Row Overlay","text":"Row Overlay Set the slotRowsOverlay property to a React element when you want the overlay to appear above the row content of the grid. The slotRowsOverlay element is rendered inside the grid's rows container. The demo below shows a rows overlay that displays a “No Rows” indicator. ::demo[No Rows Overlay=\"./demos/overlays-rows\"] The No Rows overlay code is shown below. The overlay is a positioned element rendered inside the grid's rows container element.","link":"docs/component-grid-overlays#row-overlay","depth":2},{"header":"Choosing Overlays","text":"Choosing Overlays Use the viewport overlay for general feedback content; it is typically easier to style and implement. Use the rows overlay only if: You need the overlay to extend across the full width and height of the scrollable area.Your application logic requires the overlay to be rendered inside the rows container.","link":"docs/component-grid-overlays#choosing-overlays","depth":2},{"header":"Menu Anatomy","text":"Menu Anatomy LyteNyte Grid exports the Menu component as a general-purpose dropdown list of actions. The Menu component supports nested lists, grouped items, and checkbox items. The Menu component is headless and is composed of multiple parts that must be used together. The code block below shows these parts assembled:","link":"docs/component-menu-button#menu-anatomy","depth":2},{"header":"Basic Menu","text":"Basic Menu The demo below shows a basic example of the Menu component. It demonstrates three types of menu items: Menu.Item: A standard menu item that performs an action when clicked.Menu.RadioItem: An exclusive selection radio item. Must be rendered within a Menu.RadioGroup.Menu.CheckboxItem: An inclusive selection checkbox item that can be used for toggle settings and flags. ::demo[Basic Menu Implementation=\"./demos/menu-demo\"]","link":"docs/component-menu-button#basic-menu","depth":2},{"header":"Basic Submenu","text":"Basic Submenu Use the Menu.Submenu component to group related menu items into an expandable list of options. The demo below shows a submenu in action. ::demo[Basic Submenu=\"./demos/menu-submenu-demo\"]","link":"docs/component-menu-button#basic-submenu","depth":2},{"header":"Menu Properties","text":"Menu Properties All Menu component parts accept standard HTML element properties. The properties list below describes the additional properties provided by the Menu components.","link":"docs/component-menu-button#menu-properties","depth":2},{"header":"Menu","text":"Menu Menu extends the popover component with additional logic for dropdown menus, inheriting its foundational properties.","link":"docs/component-menu-button#menu","depth":3},{"header":"Prebuilt Theming","text":"Prebuilt Theming LyteNyte Grid components are unstyled by default, so you can apply your own styles or incorporate an existing design system. LyteNyte Grid also provides prebuilt styles for every component, using the grid's theme tokens. To use these styles, import the following CSS file:","link":"docs/component-overview#prebuilt-theming","depth":2},{"header":"Data Attributes","text":"Data Attributes Like the grid itself, LyteNyte Grid components use data-attributes to expose component state to CSS. Use these attributes for custom styling and behavior. To find relevant data-attributes, inspect the rendered elements in your browser's DevTools.","link":"docs/component-overview#data-attributes","depth":3},{"header":"Slots","text":"Slots Many components accept SlotComponent props to customize rendering. These render props merge your props and event handlers with the component's internal props and handlers, so you can control the rendered output. For example, the Select All component accepts a slot property that you can use to override what the SelectAll component renders: The SlotComponents also accept a function variant. When you provide a function, the component passes additional state to that function. For example, the SelectAll component provides a toggle function that changes the selection state. The SlotComponent must be an element that accepts standard HTML props, such as onClick, or, in the case of function slots, must return an element that accepts standard HTML props.","link":"docs/component-overview#slots","depth":2},{"header":"Pill Manager Anatomy","text":"Pill Manager Anatomy The PillManager component is a headless component. Similar to LyteNyte Grid, it supports two rendering modes: Headless Mode: PillManager exposes its component parts for full custom configuration.Default Mode: PillManager renders using a built-in default configuration.","link":"docs/component-pill-manager#pill-manager-anatomy","depth":2},{"header":"Pill Row Specification","text":"Pill Row Specification PillManager requires the rows property to be an array of PillRowSpec objects. Each PillRowSpec object is rendered as a row containing pills. The PillRowSpec and PillItemSpec interfaces are shown below.","link":"docs/component-pill-manager#pill-row-specification","depth":2},{"header":"Pill Active State","text":"Pill Active State Set the active property on every PillItemSpec in a PillRowSpec. The active property determines whether a pill is enabled. The semantic meaning of active is defined by your use case. The demo below uses active to toggle column visibility, where inactive pills appear dimmed. ::demo[Pill Manager State=\"../(column)/demos/column-visibility\"] When a pill's active state changes, the PillManager calls the onPillRowChange handler. Use the provided parameters shown below to update your row state.","link":"docs/component-pill-manager#pill-active-state","depth":2},{"header":"Pill Reordering","text":"Pill Reordering Set the movable property on a pill to enable reordering. By default, a pill can be reordered within its own pill row. By setting the tags property, you can indicate that a pill may also be moved to another row. For another row to accept a pill, set the accepts property on the row to include one of the tags from the pill being moved. The demo below demonstrates pill reordering. You can drag pills between sections to activate them, or reorder pills within the same row. ::demo[Pill Manager Reordering=\"../(pivoting)/demos/pivot-rows\"] When pills are reordered, the PillManager calls the onPillRowChange event handler. The handler type is shown below. The changed property contains the PillRowSpec objects that were updated.The full property contains all PillRowSpec objects, with their pills updated to reflect the completed move.","link":"docs/component-pill-manager#pill-reordering","depth":2},{"header":"Pill Parts","text":"Pill Parts All parts are rendered using a render prop provided to the PillManager. The render prop is optional. If you do not provide it, the PillManager uses a default implementation.","link":"docs/component-pill-manager#pill-parts","depth":2},{"header":"Pill Row","text":"Pill Row The PillManager.Row component is a container for an individual pill row. Each PillRowSpec provided to the PillManager results in a rendered pill row. The PillManager.Row component requires a PillRowSpec.","link":"docs/component-pill-manager#pill-row","depth":3},{"header":"Pill Label","text":"Pill Label The PillManager.Label component renders the label for each pill row. If you do not provide a label, PillManager renders a default PillManager.Label based on the pill row's type.","link":"docs/component-pill-manager#pill-label","depth":3},{"header":"Pill Container","text":"Pill Container The PillManager.Container component renders all pills for a given row. It also acts as the drop zone for external pills. All PillManager.Pill components must be rendered inside a PillManager.Container.","link":"docs/component-pill-manager#pill-container","depth":3},{"header":"Pill","text":"Pill The PillManager.Pill component renders a single pill. A pill is the primary interaction element of the PillManager. You must render each PillManager.Pill inside a PillManager.Container.","link":"docs/component-pill-manager#pill","depth":3},{"header":"Pill Expander","text":"Pill Expander The PillManager.Expander toggles the display mode of a pill row between a scrollable view and a wrapped view. When expanded, all pills in the row are visible.","link":"docs/component-pill-manager#pill-expander","depth":3},{"header":"Vertical Pills","text":"Vertical Pills Set the orientation property on the PillManager to \"vertical\" to orient pills vertically. The demo below demonstrates this configuration. Since the PillManager is headless, you must apply the appropriate styles when rendering a vertical layout. ::demo[Vertical Pill=\"./demos/pill-manager-vertical\"]","link":"docs/component-pill-manager#vertical-pills","depth":2},{"header":"Pill Manager Styling","text":"Pill Manager Styling PillManager is unstyled by default. Import the prebuilt LyteNyte Grid styles to align PillManager with the rest of the grid. These styles also provide pill theming for each PillRowSpec type.","link":"docs/component-pill-manager#pill-manager-styling","depth":2},{"header":"Pill Manager Properties","text":"Pill Manager Properties All PillManager component parts render as HTML elements and accept standard HTML props. This section lists the additional properties that each component supports.","link":"docs/component-pill-manager#pill-manager-properties","depth":2},{"header":"Popover Anatomy","text":"Popover Anatomy LyteNyte Grid's popover is a headless component composed of several parts that you must assemble before use. The example below provides a brief overview of these parts.","link":"docs/component-popover#popover-anatomy","depth":2},{"header":"Popover Configuration","text":"Popover Configuration The demo below demonstrates a basic popover. It contains the minimum structure required for a fully accessible popover, which you can expand upon to meet your application's needs. ::demo[Basic Popover Implementation=\"./demos/popover-demo\"]","link":"docs/component-popover#popover-configuration","depth":2},{"header":"Popover Properties","text":"Popover Properties Popover elements accept standard HTML props. Some parts also support additional properties, which are documented below.","link":"docs/component-popover#popover-properties","depth":2},{"header":"Row Group Component","text":"Row Group Component LyteNyte Grid exports the RowGroupCell component to handle row group expansion and collapse. It provides the following built-in functionality: Expand and collapse group nodes.Prevent expansion for non-expandable group nodes.Display loading indicators during expansion.Display error indicators if expansion fails. LyteNyte Grid's RowGroupCell component significantly reduces boilerplate compared with a custom implementation. As shown in the demo below, the Group column uses the RowGroupCell component to render group expansion and to handle out-of-the-box group label retrieval. ::demo[Row Group Cell=\"../(client-source)/demos/client-row-aggregation-dimensions\"] The code below shows a basic example of configuring a group cell. The RowGroupCell component is a cell renderer and must be provided with all required cell renderer properties.","link":"docs/component-row-group-cell#row-group-component","depth":2},{"header":"Row Group Cell Styling","text":"Row Group Cell Styling The RowGroupCell component is unstyled by default. You can style the component using the data attributes applied to the group cell elements. If you are using one of the prebuilt grid themes, the group cell styling is already included. To create or customize group cell styles, refer to the example CSS below. The prebuilt themes use this CSS as a reference implementation.","link":"docs/component-row-group-cell#row-group-cell-styling","depth":2},{"header":"Row Group Cell Properties","text":"Row Group Cell Properties The RowGroupCell component is a cell renderer. To pass additional props, wrap RowGroupCell in a custom component and forward the props. The wrapper must implement the cell renderer interface. The example below uses this pattern to add custom leaf-row labels to the row group cell. The RowGroupCell component extends CellParamsWithIndex. The properties listed below are additional properties supported by the component.","link":"docs/component-row-group-cell#row-group-cell-properties","depth":2},{"header":"Select All Rendering","text":"Select All Rendering LyteNyte Grid exports the SelectAll component. Use it in a column header or any component with access to the grid API. The demo below shows SelectAll in the marker column header. When checked, the grid selects all rows. ::demo[Select All Implementation=\"../(column)/demos/column-marker-selection\"] The code below shows the SelectAll component. LyteNyte Grid also exports a Checkbox for rendering the indeterminate state, though you can use your own checkbox component instead.","link":"docs/component-select-all#select-all-rendering","depth":2},{"header":"Why Use Select All","text":"Why Use Select All The SelectAll component manages all selection states. LyteNyte Grid supports two selection modes: linked and isolated. Each mode uses different rules to determine whether all rows are selected. SelectAll handles those mode-specific differences and exposes a single, consistent interface for determining the overall selection status, rendering indeterminate states, and performing the correct select-all actions.","link":"docs/component-select-all#why-use-select-all","depth":3},{"header":"Single Select","text":"Single Select The simplest variant of the SmartSelect component is the single select variant. Set the kind property to \"basic\" and the trigger property to SmartSelect.BasicTrigger to use the SmartSelect component in basic select mode. The demo below demonstrates this configuration. The value and options properties are required. Each SmartSelect option must have a unique ID. ::demo[Basic Single Select=\"./demos/smart-select-basic-demo\"]","link":"docs/component-smart-select#single-select","depth":2},{"header":"Combo Select","text":"Combo Select Set the kind property to \"combo\" and the trigger property to SmartSelect.ComboTrigger to use the SmartSelect component in combobox mode. A combobox allows users to search for an item before selecting it. The demo below uses the SmartSelect component as a combobox. While this example populates the input field with the selected value, this behavior is optional. Often, a combobox updates state in other components. ::demo[Basic Combobox Select=\"./demos/smart-combo-demo\"]","link":"docs/component-smart-select#combo-select","depth":2},{"header":"Multiple Select","text":"Multiple Select Set the kind property to \"multi\" and the trigger property to SmartSelect.MultiTrigger to use the SmartSelect component in multi-select mode. Multi-select mode allows users to select multiple items. The value property must be provided as an array of options, unlike the basic select variant, which accepts a single selected value. The demo below shows multi-select. Selected items are rendered as SmartSelect.Chip components. You must use SmartSelect.Chip for selected values to ensure the SmartSelect component correctly handles keyboard navigation. ::demo[Multiple Select=\"./demos/smart-select-multi-demo\"]","link":"docs/component-smart-select#multiple-select","depth":2},{"header":"Multiple Combobox Select","text":"Multiple Combobox Select Set the kind property to \"multi-combo\" and the trigger property to SmartSelect.MultiComboTrigger to use the SmartSelect component in multi-combobox mode. Multi-combobox mode allows users to select multiple items using a combobox input. The demo below shows multi-combobox in action. Selected items are rendered as SmartSelect.Chip components. Users can type into the search input to add additional chips to the current selection. ::demo[Multi-Combobox=\"./demos/smart-multi-combo-demo\"]","link":"docs/component-smart-select#multiple-combobox-select","depth":2},{"header":"Asynchronous Select","text":"Asynchronous Select When the SmartSelect component uses a combobox trigger, the options property can return a Promise. Returning a Promise places the SmartSelect component into search mode. The demo below shows asynchronous searching. This approach is useful when options for a given search query must be fetched from a server. ::demo[Asynchronous Multi-Combobox=\"./demos/smart-multi-combo-async\"]","link":"docs/component-smart-select#asynchronous-select","depth":2},{"header":"Smart Select Properties","text":"Smart Select Properties The SmartSelect component is composed of several headless component parts. This section describes the properties for headless components that define behavior beyond standard HTML element properties.","link":"docs/component-smart-select#smart-select-properties","depth":2},{"header":"SmartSelect","text":"SmartSelect Depending on the kind property, additional properties are available. Basic Multi Select Combo Select Multi-Combo Select","link":"docs/component-smart-select#smartselect","depth":3},{"header":"Basic Tree View","text":"Basic Tree View Import TreeView from LyteNyte Grid to get started. Pass an array of path-based objects to the items property. Each item must conform to the TreeViewItem interface shown below: The items property is a flat array. The path property on each item determines the resulting tree structure. The demo below shows a basic tree view. Since TreeView is a LyteNyte Grid variant, it requires a sized container. ::demo[Tree View Implementation=\"./demos/tree-view-demo\"] The code for this tree view example is shown below. The items property is set to an array of values. You can select rows by clicking the checkbox next to each item.","link":"docs/component-tree-view#basic-tree-view","depth":2},{"header":"Tree Selection","text":"Tree Selection The TreeView component always uses linked row selection. The selection behavior matches the standard LyteNyte Grid selection model. You can provide a ref to the TreeView component to access the TreeView API, which includes the rowsSelected method for retrieving selected items. To control row selection manually, set the rowSelection property on the TreeView component. Provide an onRowSelectionChange callback to respond to selection updates. Since row selection is represented as a linked tree, you must determine which rows are selected by traversing the selection tree. The demo below demonstrates this behavior by tracking which files in the tree are selected. ::demo[Tree Selection Implementation=\"./demos/tree-view-selection\"] The code below determines selection by traversing the tree and filtering items based on their selection state. This is one simple approach to determining selected rows. You should choose an approach that matches your application's requirements.","link":"docs/component-tree-view#tree-selection","depth":2},{"header":"Custom Tree View Rendering","text":"Custom Tree View Rendering You can customize the rendered output of the TreeView component by providing a render prop as the children value. This render prop receives a set of TreeViewChildParams, shown below, which you can use to render custom content. Using the children render prop, you can add additional functionality to rows, such as item removal. The demo below demonstrates this behavior. ::demo[Custom Tree View Nodes=\"./demos/tree-view-children\"]","link":"docs/component-tree-view#custom-tree-view-rendering","depth":2},{"header":"Shadows Slot","text":"Shadows Slot LyteNyte Grid exports ViewportShadows. Pass it to slotShadows on the grid. ViewportShadows renders within the grid's viewport element. The demo below demonstrates ViewportShadows. The pinned sections show borders. Scroll horizontally or vertically to observe shadows appearing. ::demo[Viewport Shadows=\"./demos/viewport-shadows\"] The code for the viewport shadows is shown below. Note that the slotShadows property must be provided a component function and does not accept a React Element. The ViewportShadows component uses CSS to remain visible regardless of the grid's scroll position. This behavior makes the component useful for drawing pin area shadows. The shadows also avoid CSS stacking issues, which makes the component well suited for adding visual depth without shadows being clipped.","link":"docs/component-viewport-shadows#shadows-slot","depth":2},{"header":"Styling Viewport Shadows","text":"Styling Viewport Shadows LyteNyte Grid components are unstyled by default. The ViewportShadows component requires custom CSS to display visual feedback. If you are using a prebuilt LyteNyte Grid theme, the required styles are already applied. To style or adjust the ViewportShadows component, target its data attributes. The CSS used by the prebuilt themes is shown below and can be used as a reference for custom styling. The data-ln-y-status and data-ln-x-status data attributes have one of the following values: \"none\": Set when the grid has no scroll applied. x represents horizontal scroll and y represents vertical scroll.\"partial\": Set when some scroll has been applied to the grid.\"full\": Set when the grid is fully scrolled in the direction. x represents horizontal scroll and y represents vertical scroll.","link":"docs/component-viewport-shadows#styling-viewport-shadows","depth":2},{"header":"Shadows Properties","text":"Shadows Properties The ViewportShadows component accepts the properties listed below. To apply these properties, create an intermediate component. For example:","link":"docs/component-viewport-shadows#shadows-properties","depth":2},{"header":"Arrow Download","text":"Arrow Download The demo below exports grid data to an Arrow file. Arrow is a binary format, so use an Arrow viewer to inspect the output. ::demo[Arrow Export & Download=\"./demos/arrow-export\"] To match Arrow's columnar format, the code constructs an object of arrays, with one array per column. It passes the object to tableFromArrays from apache-arrow to create a table. Finally, it converts the table to a byte buffer, wraps the buffer in a Blob, and downloads the file.","link":"docs/export-arrow#arrow-download","depth":2},{"header":"Clipboard Workflow","text":"Clipboard Workflow The demo below shows clipboard interactions. Click and drag to select cells. Press Ctrl C to copy, Ctrl X to cut, and Ctrl V to paste. ::demo[Clipboard=\"./demos/clipboard\"] The code below shows how to handle copy operations. Expand the code in the demo frame to view the full implementation. The handleCopy function uses the current selection and the exportData method to copy grid cell content to the clipboard. When you pass a rect argument, exportData returns only the rows and columns that fall within the specified rectangle. The handleGridUpdate function parses update data and writes it to the grid using api.editUpdateCells. This update data comes from the event handler attached to the grid. :::note This sample prioritizes conciseness. In production, treat clipboard data as untrusted input and validate it using libraries like Zod before applying updates. See the Cell Editing guide for details. :::","link":"docs/export-clipboard#clipboard-workflow","depth":2},{"header":"CSV Download","text":"CSV Download The demo below exports grid data to a CSV string, then downloads it as a CSV file. ::demo[CSV Export & Download=\"./demos/csv-export\"] The example below demonstrates a basic CSV export implementation. For robust serialization that handles all edge cases, use a dedicated library like csv-parser.","link":"docs/export-csv#csv-download","depth":2},{"header":"Excel Download","text":"Excel Download The demo below lets you download an Excel file containing all grid data. It uses ExcelJS to create a Blob and trigger the download. ::demo[Excel Export & Download=\"./demos/excel-export\"] The implementation uses exportData to retrieve the current view of cells.","link":"docs/export-excel#excel-download","depth":2},{"header":"Export Data","text":"Export Data The exportData method exports a rectangular area of data from the grid. This rectangular area is defined by a start and end column and a start and end row, with the end values being exclusive. The rectangle interface is shown below. If you call exportData without any arguments, the grid exports data for a rectangle that covers the entire grid. The examples below show the two ways to call exportData. The exportData method returns a Promise object. The interface for this result is shown below. Each property on ExportDataRectResult is an array, and the length of each array matches the size of the requested rectangle. LyteNyte Grid performs the work of returning only the data for the requested columns and rows.","link":"docs/export-overview#export-data","depth":2},{"header":"Export Files","text":"Export Files Use exportData primarily for file exports, though the API supports broader data extraction tasks. For format-specific implementations, see the guides below: Excel exportCSV exportParquet exportArrow exportClipboard export","link":"docs/export-overview#export-files","depth":2},{"header":"Parquet Download","text":"Parquet Download The demo below exports grid data to a Parquet file. Parquet is a binary format, so use a Parquet viewer to inspect the output. ::demo[Parquet Export & Download=\"./demos/parquet-export\"] To match Parquet's columnar format, the code below constructs an object of arrays, with one array per column. It passes the object to parquetWriteBuffer, which returns an ArrayBuffer. Finally, it wraps the buffer in a Blob and downloads the file.","link":"docs/export-parquet#parquet-download","depth":2},{"header":"Debounce Filter Inputs","text":"Debounce Filter Inputs Many filters require user input. If you apply a filter immediately as the user enters values, ensure that you debounce the input. Applying filters on every keystroke is not recommended, since each change often requires re-evaluating all filter operations for every row. For more information on debouncing as a pattern, see the MDN Debounce guide.","link":"docs/filtering-best-practices#debounce-filter-inputs","depth":2},{"header":"Handle Null Values","text":"Handle Null Values Always account for null values. In your filter logic, make a deliberate decision about whether null values should be kept or removed. The presence of null values is often an afterthought, but handling them incorrectly can make filter implementations more complex and less predictable.","link":"docs/filtering-best-practices#handle-null-values","depth":2},{"header":"Ensure Filter Efficiency","text":"Ensure Filter Efficiency Filters run against every row, so keep filter functions efficient and avoid heavy computation inside filter logic. Some LyteNyte Grid row sources accept an array of filter functions, such as the client row source. The grid evaluates filter functions in the order provided. Order filters from most efficient to least efficient to maximize performance.","link":"docs/filtering-best-practices#ensure-filter-efficiency","depth":2},{"header":"Display Filter Feedback","text":"Display Filter Feedback Always provide a visible indication that filters are active. This feedback can be as simple as a filter icon or a subtle but noticeable color indicator. Use consistent visual cues for filter feedback, and always provide a clear way to remove or reset filters.","link":"docs/filtering-best-practices#display-filter-feedback","depth":2},{"header":"Date Filters","text":"Date Filters LyteNyte Grid provides flexible filter capabilities that let you define custom date filters. You can check whether two dates are equal, whether a date falls within a specific range, or whether a date falls within a specific time period, such as the first quarter of the year. Filtering dates is more complex than filtering other data types, since dates can have many representations, formats, and timezones to contend with. In this guide, dates are represented as ISO 8601 date strings. :::note The demos and code in this section use the data-fns library to simplify date filter implementations. The data-fns library is not required for date filters, but it is recommended. You may also use an alternative library, such as Luxon. ::: To create a date filter, define a function that receives a row node and returns true to keep the row or false to remove it. For example, the following function filters a list of orders and keeps only orders with a sale date in 2025. Click the Sales in 2025 switch to toggle the filter state. ::demo[Date Filter=\"./demos/date-filter\"]","link":"docs/filtering-dates#date-filters","depth":2},{"header":"Date Filter Model","text":"Date Filter Model Define date filters dynamically rather than relying on predefined logic. LyteNyte Grid lets you define a filter model and a set of operations to build custom filters. This section presents one approach. Design the filter model that best fits your application's requirements. Begin by defining the type representation for your date filter. The code below defines a basic filter model you can use to create a filter function for the client row data source: Open the filter popover by clicking the funnel icon on the Sale Date column. ::demo[Date Filter User Interface=\"./demos/date-filter-ui\"] :::note The demo dynamically generates date values for each row based on the current date. It demonstrates the full range of supported date filters. Since the demo generates dates at runtime, the values change depending on the current date. ::: The code below defines the filter model. It creates filter model state and passes it to the grid API as an extension. The demo also includes a filter UI for applying filters interactively. When a user applies a filter, useMemo creates a new filterFn, which you then pass to the client data source. See the filter.tsx file in the demo's expanded code for the logic that builds the filter UI.","link":"docs/filtering-dates#date-filter-model","depth":2},{"header":"Basic Number Filtering","text":"Basic Number Filtering LyteNyte Grid provides flexible filter capabilities that let you define any type of number filter. You can check whether a value is equal to, less than, greater than, or evaluate any other numeric comparison. To create a number filter, define a function that receives a row node and returns true to keep the row or false to remove it. For example, the following function filters a list of products and keeps only rows where the product price is greater than $50: Click the Price Greater Than $300 switch to toggle the filter state. ::demo[Number Filtering=\"./demos/number-filtering\"]","link":"docs/filtering-numbers#basic-number-filtering","depth":2},{"header":"Number Filter Model","text":"Number Filter Model Define number filters dynamically rather than relying on predefined logic. LyteNyte Grid lets you define a filter model and a set of operations to build custom filters. This section presents one approach. Design the filter model that best fits your application's requirements. Begin by defining the type representation for your number filter. The code below defines a basic filter model you can use to create a filter function for the client row data source: Open the filter popover by clicking the funnel icon on the Price column. ::demo[Number Filter User Interface=\"./demos/number-filter-ui\"] The code below defines the filter model. It creates filter model state and passes it to the grid API as an extension. The demo also includes a filter UI for applying filters interactively. When a user applies a filter, useMemo creates a new filterFn, which you then pass to the client data source. See the filter.tsx file in the demo's expanded code for the logic that builds the filter UI.","link":"docs/filtering-numbers#number-filter-model","depth":2},{"header":"Implementing Quick Search","text":"Implementing Quick Search Implement quick search by passing a filter function to the grid that checks whether a cell value contains the search string. The demo below uses a case-insensitive inclusion match. For fuzzy search, integrate a library like Fuse.js. ::demo[Quick Search Filter=\"./demos/quick-search\"] The demo tracks a query value in state, generates a corresponding filter function, and passes it to the data source. An input element updates the query value. The input is debounced to ensure that the filter does not run on every keystroke, as shown in the following example.","link":"docs/filtering-quick-search#implementing-quick-search","depth":2},{"header":"Basic Set Filtering","text":"Basic Set Filtering A set filter checks whether a cell's value is a member of a defined set of values. A set filter supports two possible checks, which are inverses of each other: Inclusion: Checks whether the cell's value exists in the set of allowed values.Exclusion: Checks whether the cell's value does not exist in the set of disallowed values. Implementing a set filter in LyteNyte Grid involves creating a filter function that captures the set of values and accepts a row node. The filter function must return true to keep the row or false to remove it. Type a filter query to select product Name values to display. The grid shows only rows that match the selected values. ::demo[Basic Set Filtering=\"./demos/set-filter\"]","link":"docs/filtering-set-filters#basic-set-filtering","depth":2},{"header":"Tree Set Filters","text":"Tree Set Filters A tree set filter represents filter options as a structured tree. Each item in the tree has a parent-child relationship with other items. Tree set filters allow the grid to filter rows based on an arbitrary hierarchical relationship. A common use case for tree set filters is date filtering. Date values are represented by year, month, and day. The demo below demonstrates a tree set filter. Click the funnel icon on the Sale Date column to open the filter popover. ::demo[Tree Set Filter=\"./demos/tree-set-filter\"] The tree set filter uses the Tree View component exported by LyteNyte Grid. The Tree View component creates a hierarchical structure from a set of paths. The following code, from the demo's filter.tsx file, generates those paths. LyteNyte Grid represents selection state using the RowSelectionLinked type. By default, all items are selected, and the grid filters out any items that users deselect. The code below determines which rows should be excluded. The main logic lives in the excludeSets memo. The code traverses the selection tree to identify deselected items. As the traversal progresses, the logic tracks the selection state of each node to determine whether a row should be filtered out. :::note Tree View wraps LyteNyte Grid, so it uses the same row selection model. For details, see the Row Selection guide. :::","link":"docs/filtering-set-filters#tree-set-filters","depth":2},{"header":"Grid API","text":"Grid API The LyteNyte Grid API provides methods for programmatically handling common grid functionality, such as selecting rows or toggling row groups. The grid passes an API reference to every component callback, including cell and row detail renderers. You can also obtain a reference to the API by providing a ref to the Grid component, as shown below:","link":"docs/grid-api-extensions#grid-api","depth":2},{"header":"Extending the API","text":"Extending the API Use the apiExtension property to add additional properties to the LyteNyte Grid API. Extending the API lets you add bespoke functionality on top of the existing API methods. For example, you can add a notify function as follows: The value of the apiExtension property must be an object. LyteNyte Grid assigns the extension value onto a stable API reference. The API object reference in LyteNyte Grid never changes, so it is not a reactive value. Changes to the value of apiExtension apply to the API object, but these changes do not automatically trigger cell re-renders.","link":"docs/grid-api-extensions#extending-the-api","depth":2},{"header":"Function API Extension","text":"Function API Extension The apiExtension property can accept a function that returns an object containing the additional API methods to add to the grid API. The function form is called with a reference to a partial version of the grid API. You can safely capture this reference in your extension methods, but do not call any API methods inside the extension function itself. The function form of the apiExtension is useful when the methods you add to the API need to call the grid API to perform work. For example, you can add the ability to rename header cells by extending the API with an updateHeaderName method. This method can then call api.columnUpdate to change the name of a column. The demo below illustrates this approach. Try double-click the column header to rename it. ::demo[Renaming Column Headers=\"./demos/grid-api-function\"]","link":"docs/grid-api-extensions#function-api-extension","depth":3},{"header":"Supplying Reactive Values","text":"Supplying Reactive Values Since the API reference never changes, any component that reads values from the API object will not re-render when the apiExtension value changes. You may want to supply the API with a reactive value, such as application state. The naive approach might look like this: You may expect clicking IncrementRenderer to increment the displayed count, but it won't. When you click the button, the count does increment, but cells do not re-render because the API object reference remains stable. To make this work, you need to inject a hook for the state value. The exact approach varies depending on how you create state. If you use a state management library like Zustand or Jotai, you can add the store or atom to the API extension and then read the value from the store/atom in your component renderer. For example, with Jotai you can do the following:","link":"docs/grid-api-extensions#supplying-reactive-values","depth":2},{"header":"Piece Utility","text":"Piece Utility LyteNyte Grid provides the usePiece hook to simplify extending the API with reactive state. This hook is convenient if you are not using an external state management library. The usePiece hook creates a reactive watcher for the value you provide. You can then share the watcher with other components, which can selectively update when the watched value changes. You can use usePiece to extend the API as follows: :::note The second parameter to usePiece is optional. If you provide it, the returned piece becomes writable. :::","link":"docs/grid-api-extensions#piece-utility","depth":3},{"header":"Typing the API Extension","text":"Typing the API Extension You can provide type information for API extensions using the GridSpec interface. The example below shows the basic idea. See our TypeScript guide for further information.","link":"docs/grid-api-extensions#typing-the-api-extension","depth":2},{"header":"API & Row Source","text":"API & Row Source LyteNyte Grid expects you to provide a value for the rowSource property. The grid uses this property to define the row source implementation. The LyteNyte Grid API also includes the properties of the RowSource you provide, so the grid API includes both your apiExtension properties and the RowSource properties. The code below results in an API that includes the ds value. The row source provides useful functionality such as row retrieval and editing.","link":"docs/grid-api-extensions#api--row-source","depth":2},{"header":"Basic Text Filtering","text":"Basic Text Filtering LyteNyte Grid provides flexible filter capabilities that let you define any type of text filter. You can filter on contains, begins with, ends with, exact matching, or regex matching. To create a text filter, define a function that receives a row node and returns true to keep the row or false to remove it. For example, the following function filters a list of products and keeps only rows where the product name contains Xbox: Click the Show Xbox Only switch to toggle the filter state. ::demo[Text Filtering=\"./demos/text-filtering\"]","link":"docs/filtering-text#basic-text-filtering","depth":2},{"header":"Filter Modifiers","text":"Filter Modifiers Apply modifiers within the text filter function to control matching behavior. For example, convert values to lowercase to enable case-insensitive filtering: Expand filter logic to ignore punctuation, trim whitespace, and normalize accented characters, such as mapping \"á\" to \"a\":","link":"docs/filtering-text#filter-modifiers","depth":3},{"header":"Text Filter Model","text":"Text Filter Model Define text filters dynamically rather than relying on predefined logic. LyteNyte Grid lets you define a filter model and a set of operations to build custom filters. This section presents one approach. Design the filter model that best fits your application's requirements. Begin by defining the type representation for your text filter. The code below defines a basic filter model you can use to create a filter function for the client row data source: Open the filter popover by clicking the funnel icon on the Product, Customer, or Email columns. ::demo[Text Filter User Interface=\"./demos/text-filter-ui\"] The code below defines the filter model. It creates filter model state and passes it to the grid API as an extension. The demo also includes a filter UI for applying filters interactively. When a user applies a filter, useMemo creates a new filterFn, which you then pass to the client data source. See the filter.tsx file in the demo's expanded code for the logic that builds the filter UI.","link":"docs/filtering-text#text-filter-model","depth":2},{"header":"Pixel Value","text":"Pixel Value Fixed pixel values for height and width provide the simplest sizing option but limit flexibility. In most cases, set a fixed height and allow the width to adjust based on the parent container. Set a fixed width only when necessary. ::demo[Pixel Height and Width=\"./demos/pixel-height-container\"] The height of the grid viewport comes from the div that contains it, which uses the following code:","link":"docs/grid-container#pixel-value","depth":2},{"header":"Flex Height Containers","text":"Flex Height Containers Flexbox layouts distribute space dynamically. When you assign flex: 1 to a Flexbox child, it expands to fill the available space. Since Flexbox containers grow with their content, wrap LyteNyte Grid in an inner div with position: absolute, width: 100%, and height: 100% to prevent the grid from increasing the container's height. The grid then fills the space without overflow. ::demo[Flex Height Container=\"./demos/flex-height-container\"] The flex height container uses a series of nested divs, as shown below:","link":"docs/grid-container#flex-height-containers","depth":2},{"header":"Grid Height Containers","text":"Grid Height Containers CSS Grid (not to be confused with LyteNyte Grid) provides a flexible layout system. Within a grid container, LyteNyte Grid expands to fill its assigned area while remaining properly contained. ::demo[Grid Height Container=\"./demos/grid-height-container\"] The grid height container uses the grid-template-rows property, as shown below: Other CSS sizing techniques also work well with LyteNyte Grid. Choose the approach that best fits your layout requirements.","link":"docs/grid-container#grid-height-containers","depth":2},{"header":"Grid Anatomy Overview","text":"Grid Anatomy Overview The code below offers a high-level overview of the individual parts of the grid component anatomy. Treat this example as a general outline of the grid structure. Apart from the LyteNyte grid's default mode, Headless Mode has two variants: Headless Mode: A variant that only renders the top-level containers.Full Headless Mode: A fully rendered variant that renders every cell.","link":"docs/grid-headless-parts#grid-anatomy-overview","depth":2},{"header":"Grid","text":"Grid The Grid component acts as the root component for all grid elements. It accepts grid props as state and provides its children with the necessary context for LyteNyte Grid to function. The Grid component also exposes the other component parts as properties.","link":"docs/grid-headless-parts#grid","depth":2},{"header":"Viewport","text":"Viewport The Grid.Viewport component creates the element that acts as the overflow parent for the grid. As its name suggests, it defines the visible area of the grid and displays rows and columns based on the scroll position. The viewport automatically sizes to fit its container. See the Responsive Container guide for details on configuring containers for the grid. Adding Grid.Viewport to the grid component gives us: Grid.Viewport renders a div element. It accepts all standard div props, including className and style. LyteNyte Grid also applies inline styles for grid sizing.","link":"docs/grid-headless-parts#viewport","depth":2},{"header":"Header","text":"Header LyteNyte Grid has a single header container, rendered by the Grid.Header component. You can render the Grid.Header component in two ways: Without children: The header component uses the default configuration to render header groups and header cells.With children: The header component accepts a render prop as its children, which gives you an opportunity to add customizations before rendering the header group and header cell elements. Rendering Grid.Header without children is the most straightforward approach. The Grid.Header component renders a normal div element and accepts all standard div props.","link":"docs/grid-headless-parts#header","depth":2},{"header":"Header Render Prop","text":"Header Render Prop Passing a render prop as the children of the Grid.Header component allows for more fine-grained control over how the header renders. When using this approach, the render prop must return a Grid.HeaderRow, and that component must render the header cells as its children. The render prop function receives an array of LayoutHeader items. Each LayoutHeader item describes the layout of an individual header cell, such as whether the cell is pinned or whether the cell belongs to a column group, a normal header, or a floating header. You can use this information to apply custom logic, though the default rendering behavior is usually the best place to start.","link":"docs/grid-headless-parts#header-render-prop","depth":3},{"header":"Header Row","text":"Header Row When using a custom render prop for Grid.Header, the function must return a Grid.HeaderRow element. The total number of header rows equals the maximum column group depth, plus one additional row for the floating row (if enabled), and another row for the column header row. The Grid.Header component renders a normal div element and accepts all standard div props. The Grid.Header render prop receives an array of header cells as its first argument. You must use these header cells to render the children of the Grid.HeaderRow, as shown in the example below.","link":"docs/grid-headless-parts#header-row","depth":3},{"header":"Header Cells","text":"Header Cells Within each , you render header cells. These cells represent column headers. LyteNyte Grid provides two header cell components: Grid.HeaderGroupCell: Renders the cell for a column group.Grid.HeaderCell: Renders the cell for a column header or a floating header cell. The render prop of Grid.Header receives an array of cells to render. Each item in this array represents a single header cell. You must render these cells inside a Grid.HeaderRow element, as shown below. You can determine which component to render by checking the cell's kind property. When the cell's kind is \"group\", render a Grid.HeaderGroupCell. Otherwise, render a Grid.HeaderCell. There are several details to note here. For each cell, choose the appropriate header component base on the kind property. For Grid.HeaderGroupCell, use c.idOccurrence as the key instead of c.id. A column group can be split across the header, so c.id may repeat. React requires keys to be unique within a list, and c.idOccurrence guarantees uniqueness.","link":"docs/grid-headless-parts#header-cells","depth":3},{"header":"Rows Container","text":"Rows Container Use the Grid.RowsContainer component to render the grid's rows. Grid.RowsContainer is similar to Grid.Header in that it: Acts as the container for the grid rows.Renders a normal div element and accepts all standard div props. An updated example is shown below:","link":"docs/grid-headless-parts#rows-container","depth":2},{"header":"Top, Center, and Bottom Rows","text":"Top, Center, and Bottom Rows Grid.RowsContainer is the parent for all grid rows. LyteNyte Grid splits rows into three component sections: Grid.RowsTop: Rows that are pinned to the top of the viewport, after the header.Grid.RowsCenter: Scrollable rows that render after the top rows.Grid.RowsBottom: Rows that are pinned to the bottom of the viewport. Each section component accepts an optional render prop as children, giving you more fine-grained control over how rows and cells render. Like Grid.Header, this approach provides two ways to render rows: Without a Render Prop: Default mode and the simplest approach that renders the row section without a render propWith a Render Prop: Provide a render prop as children to a row section component to gain more control over row rendering. The code snippet below demonstrates the default mode structure, so it's equivalent to rendering .","link":"docs/grid-headless-parts#top-center-and-bottom-rows","depth":2},{"header":"Rows Render Prop","text":"Rows Render Prop The render prop receives a LayoutRow item. Each LayoutRow describes the type of row to render, along with properties such as rowIndex and rowPin. You can use these values to apply row-specific logic. There are two types of grid row components: Grid.RowFullWidth: Renders a row with a single cell that spans the width of the viewport.Grid.Row: Renders a row that must be provided an array of Grid.Cell elements as children. Each Grid.Cell element renders a single cell in the viewport. Use the LayoutRow passed to the render prop to determine which row type to render, as shown below.","link":"docs/grid-headless-parts#rows-render-prop","depth":3},{"header":"Row Cells","text":"Row Cells All rows contain cells except for full-width rows. The cells property on LayoutRow is an array of cells that you render using Grid.Cell. Updating the row section component results in the following: The cells must be rendered as children of a Grid.Row component. Each Grid.Cell renders a div element and accepts all standard div props.","link":"docs/grid-headless-parts#row-cells","depth":3},{"header":"Putting It All Together","text":"Putting It All Together Everything so far has focused on assembling the grid. The example below shows a full working setup that combines all the parts covered in the previous sections. The demo includes rows pinned to the top and bottom, column groups, and the full row structure. ::demo[Headless Component Parts=\"./demos/grid-headless-components\"]","link":"docs/grid-headless-parts#putting-it-all-together","depth":2},{"header":"Why Use the Compiler?","text":"Why Use the Compiler? LyteNyte Grid accepts props that are not simple primitive JavaScript values. For example, columns property is provided as an array. When you pass non-memoized props to the grid, LyteNyte Grid may perform unnecessary re-renders, degrading performance. To illustrate, the following simple code can result in performance loss. Every time the component re-renders, React creates a new columnBase object. When the columnBase value changes, LyteNyte Grid needs to recompute the row and cell layout. As a result, every element in the grid will re-render. One re-render won't impact performance, but if the component re-renders many times, the performance of your application will quickly degrade. It's worth noting that this performance issue is not specific to LyteNyte Grid. React behaves this way, and every React developer must address it whether or not they use LyteNyte Grid. The fix is simple: memoize the value using useMemo. Since memoizing values is so common, and in complex applications becomes difficult to manage, the React team created the React compiler to automatically optimize React code. When you use the React compiler, you no longer need to worry about memoizing values manually. As a result, code like the following is no longer a performance bottleneck. The compiler will optimize it for you. For more on the React compiler, see the React introduction docs. If the React compiler works for your project, it is recommended to adopt it.","link":"docs/grid-react-compiler#why-use-the-compiler","depth":2},{"header":"Props as State","text":"Props as State LyteNyte Grid accepts state via props passed to the Grid component. The grid view always reflects the props you provide. The code below shows a basic example of passing columns to the grid. You should already be familiar with React, and passing props to components in this manner should be instinctive. If you use TypeScript, TypeScript will type check all props passed to the grid. Since some props use generic type parameters, you may need to provide a concrete type for the grid. The example below shows one case. See our TypeScript guide for more details.","link":"docs/grid-reactivity#props-as-state","depth":2},{"header":"Changing Prop Values","text":"Changing Prop Values Since LyteNyte Grid accepts props for its state, updating the state of the grid is done by updating the prop values passed to the grid. For example, to configure the row height of the grid, pass different rowHeight values to the grid. You can store the rowHeight value in React state using the useState hook. ::demo[Change Row Height=\"./demos/grid-reactivity-row-height\"]","link":"docs/grid-reactivity#changing-prop-values","depth":3},{"header":"Controlled State","text":"Controlled State Some operations in LyteNyte Grid can trigger a state update. LyteNyte Grid can manage these properties as either controlled or uncontrolled. These properties include: rowDetailExpansionscolumnGroupExpansionsrowGroupColumn When one of these properties updates, the grid calls the corresponding change handler (if you provide one). For example, you can make row detail expansions controlled by providing both rowDetailExpansions and onRowDetailExpansionsChange props to the grid. As demonstrated in the demo below: ::demo[Controlled Row Detail Expansions=\"./demos/grid-reactivity-controlled-state\"] :::note Most state properties become controlled when you provide a value for that property (providing a change handler is optional). For example, providing rowDetailExpansions is enough to prevent the grid from changing row detail expansions internally. This behavior is similar to setting the value prop on an input element without a corresponding onChange handler: the value will never change based on user input. One exception is the columns property, which is always controlled. If you want the grid to update columns, you must provide an onColumnsChange handler. :::","link":"docs/grid-reactivity#controlled-state","depth":2},{"header":"Memoizing State","text":"Memoizing State Many properties you pass to LyteNyte Grid are objects, functions, or arrays. These properties should have stable references to prevent unnecessary re-renders. The easiest way to ensure stable references is to define the value outside the component. If you cannot do that, use React's useMemo and useCallback hooks: Avoid creating and passing objects in the render path of a component. A common performance problem is defining the columnBase prop as a plain object: This practice is not specific to LyteNyte Grid. It is a core part of how React works. Read the useMemo guide in the React documentation for further guidance. :::tip React's new compiler can automatically memoize values, removing the mental overhead of remembering to memoize manually. See our React Compiler guide to learn how to use LyteNyte Grid with the new compiler. :::","link":"docs/grid-reactivity#memoizing-state","depth":2},{"header":"What Is Virtualization","text":"What Is Virtualization Virtualization determines which elements are visible in the viewport and renders only those elements. The grid does not render elements outside the viewport. Whenever the viewport changes, such as during scrolling or resizing, the grid recalculates the visible range and updates the rendered elements. In this guide, virtualization refers to UI rendering and not to hardware or OS-level virtualization. Despite the similarity in naming, these are distinct concepts with no overlap. LyteNyte Grid virtualizes rows and columns only displaying the cells that are visible within the viewport. LyteNyte Grid's virtualization additionally renders some rows above and below the viewport, and some columns to the left and right of the viewport to improve the perceived performance of scrolling. Virtualization serves as the grid's primary performance optimization, and LyteNyte Grid enables it by default. To see virtualization in action, inspect the grid below using your browser's developer tools. As you scroll, you will see rows mount and unmount. That behavior shows virtualization at work. ::demo[Virtualized Grid=\"../(introduction)/demos/getting-started\"] You can implement virtualization in several ways. LyteNyte Grid supports advanced scenarios such as variable row heights, row detail areas, cell and row spans, and pinned rows and columns. To learn more about general virtualization techniques, see this List Virtualization guide on patterns.dev.","link":"docs/grid-virtualization#what-is-virtualization","depth":2},{"header":"Virtualization Configuration","text":"Virtualization Configuration You can customize or disable the grid's virtualization behavior. LyteNyte Grid renders all rows and columns visible in the viewport, along with a small number of rows and columns outside the viewport. These out-of-view elements form the grid's “overscan.” You can adjust overscan using four grid properties: rowOverscanToprowOverscanBottomcolOverscanStartcolOverscanEnd Since overscan affects only out-of-view content, changes may not appear obvious. To confirm overscan values, inspect the DOM and count the rendered rows outside the viewport. This is demonstrated in the example below: ::demo[Row Overscan Configuration=\"./demos/grid-overscan\"]","link":"docs/grid-virtualization#virtualization-configuration","depth":2},{"header":"Disabling Virtualization","text":"Disabling Virtualization If your dataset is small (fewer than ~100 rows), you may disable virtualization entirely. Use the virtualizeRows and virtualizeCols properties to turn virtualization off. ::demo[Disabling Virtualization=\"./demos/grid-no-virtualization\"] In the demo, the dataset includes only 40 rows and a limited number of columns. Rendering everything at once reduces performance for larger datasets, which holds true for all data grids, not just LyteNyte Grid. You may need to disable virtualization when printing the grid. Because virtualization removes out-of-view rows and columns from the DOM, printed output includes only the rendered content. Turning off virtualization ensures the full dataset appears in the printed result.","link":"docs/grid-virtualization#disabling-virtualization","depth":3},{"header":"Virtualization Considerations","text":"Virtualization Considerations Virtualization significantly improves performance, but it introduces several important considerations: Grid State: Since the grid mounts and unmounts row and cell components as they enter and exit the viewport, any React state stored inside a cell component disappears when that component unmounts. Therefore, it's important to maintain any required state higher in the component tree.DOM Virtualization: Virtualized rows and columns do not exist in the DOM. Browser-level “find in page” cannot match cells outside the viewport, so any DOM queries only return rendered elements.Scroll Speed: Rapid scrolling, such as dragging the scrollbar to the bottom, may reveal a momentary background flash before new content appears. This effect occurs because scrolling runs on a separate thread in modern browsers. The scroll position updates before the main thread paints the new content. :::note Some grids mitigate flashes during rapid scrolling by translating content on the main thread, but that approach causes frame drops and reduces overall application performance. :::","link":"docs/grid-virtualization#virtualization-considerations","depth":2},{"header":"Filtering Infinite Rows","text":"Filtering Infinite Rows To filter rows on the server, define a filter model and include it in each data request. Include the filter model in the queryKey of the useServerDataSource hook. Whenever the filter model changes, the grid requests a new set of rows from the server as the user scrolls. The following demo passes a custom filter model to the queryKey of the server data source. Click the funnel icon in a column header to apply a filter. ::demo[Filter Infinite Rows=\"./demos/server-filtering\"] For more guidance on filtering rows on the server, see the Server Row Filtering guide.","link":"docs/infinite-rs-row-filtering#filtering-infinite-rows","depth":2},{"header":"Sorting Infinite Rows","text":"Sorting Infinite Rows To sort rows on the server, define a sort model and send it with each request. The useServerDataSource hook accepts the sort model as part of the queryKey. The sort model can use any structure that fits your application. In most cases, match the format your server or database expects. The demo below shows infinite row sorting. Click a column header to update that column's sort state. ::demo[Sort Infinite Rows=\"./demos/server-sorting\"] The demo code applies sort state to columns. By extending the grid's API, you can update the sort property on each column directly. The extended API then applies the appropriate sort when the user clicks a column header.","link":"docs/infinite-rs-row-sorting#sorting-infinite-rows","depth":2},{"header":"Infinite Row Loading","text":"Infinite Row Loading Infinite row loading detects when a user scrolls near the end of the viewport. Once the scroll position reaches a defined threshold, the grid requests the next data slice. The example below uses the grid's events property to register a scrollEnd listener. The listener checks whether the scroll position is within 100px of the total scroll height. This example shows one approach to implementing infinite row loading. The key implementation detail is creating a request for the next data slice and calling ds.pushRequests to fetch it. For more details, see the Data Interface guide. The demo below demonstrates infinite data loading using this approach. When you scroll near the end of the grid, the grid loads additional rows after a brief delay. ::demo[Infinite Scrolling=\"./demos/basic-server-data\"]","link":"docs/infinite-rs-rows#infinite-row-loading","depth":2},{"header":"Our Motivation","text":"Our Motivation Before LyteNyte Grid, we were trapped in a cycle of frustration with bloated, brittle, and over-engineered data grid libraries. Every new project became a ritual of fighting APIs that felt like they were written by a committee that never used React. Here's what we kept running into again and again: Customization was a nightmare. Clunky, opaque APIs made even basic tweaks feel like defusing a bomb. Two-way data bindings and state sync issues between React and the grid... we've seen it all.Server data loading was a disaster. Optimistic updates, partial rendering, and caching never worked properly, or worse, worked sometimes, which is somehow more dangerous.Performance collapsed under pressure. Beyond trivial datasets, most grids fell apart. Virtualization failed. Re-renders multiplied. Main thread got blocked. Users rage-quit.Breaking changes broke more than code. New versions came with surprises, and not the fun kind. We were refactoring the same grid logic every quarter just to stay afloat.Styling was their way or no way. We were forced to adopt unfamiliar styling systems just to make things look half-decent, adding yet another layer of complexity.Bundle sizes were obscene. Grid libraries ballooned app load times by 1-3 seconds, sometimes longer. So… We patched, duct-taped, forked, and cursed. Over time, our quick fixes turned into long-term liabilities. Technical debt grew. Dev velocity dropped. Maintenance costs soared. All because the tools we relied on couldn't keep up. We built LyteNyte Grid to end that cycle.","link":"docs/intro-getting-started#our-motivation","depth":2},{"header":"Why LyteNyte Grid Stands Out","text":"Why LyteNyte Grid Stands Out At the heart of LyteNyte Grid is a commitment to the developer and user experience based on the principle of 'seamless simplicity.' Here's why we stand out: ⚛️ Clean, Declarative API: LyteNyte Grid exposes a minimal, declarative API aligned with React's data flow and state model. No wrappers. No adapter layers. No awkward integrations. Only cleaner, more maintainable code.📦 Tiny Bundle Size: Core edition is a tiny 30kb gzipped, while the PRO edition weighs only 40kb gzipped, so you no longer have to choose between advanced functionality and a fast user experience.⚡ Unrivaled Speed: LyteNyte can handle 10,000+ updates per second and render millions of rows. Our reactive state architecture means performance doesn't degrade when paginating, filtering, or pulling from the server.🧩 Headless by Design, Components Included: An industry first. Ultimate flexibility to choose between our pre-styled themes or drop into full headless mode for 100% control. Covering any use case you may have for a data table.🏢 Feature-Rich, Enterprise Ready: Handles the most demanding workflows with a comprehensive feature set that includes pivot tables, tree data, server-side loading, custom cell rendering, rich cell editing, and more, all from a single package, giving you one consistent API to build with.🫶 Simple Licensing, Transparent Support: Straightforward licensing options. All support is handled publicly on GitHub. You get complete transparency into our response times.","link":"docs/intro-getting-started#why-lytenyte-grid-stands-out","depth":2},{"header":"Core Edition vs. PRO Edition","text":"Core Edition vs. PRO Edition LyteNyte Grid is available in two editions: Core and PRO. LyteNyte Grid PRO is built on top of LyteNyte Grid Core, so it includes all Core features plus advanced capabilities for the most demanding enterprise use cases. This architecture ensures a seamless upgrade path. You can start with Core and switch to PRO later without refactoring, since it's a non-breaking, drop-in replacement. LyteNyte Core Edition: Free, open source (Apache 2.0), and genuinely useful. Includes essential features such as sorting, filtering, row grouping, column auto-sizing, detail views, data exporting, and others.LyteNyte Grid PRO Edition: A commercial edition (EULA) with advanced capabilities like server data loading, column and filter manager components, tree data, column pivoting, and more sophisticated data table tools. To determine if a feature is exclusively part of the PRO edition, look for the icon next to the feature name on the navigation bar. For a complete feature comparison between Core and PRO, check out our pricing page.","link":"docs/intro-getting-started#core-edition-vs-pro-edition","depth":2},{"header":"Quick Start","text":"Quick Start In this guide, you will build a data table inspired by the log tables in Vercel and DataDog. ::demo[Getting Started=\"./demos/getting-started\"] This demo shows the final output of the guide. If you prefer to jump straight to the complete code, fork the working demo by clicking the StackBlitz or Code Sandbox icon under the code frame.","link":"docs/intro-getting-started#quick-start","depth":2},{"header":"Installing LyteNyte Grid","text":"Installing LyteNyte Grid This guide works with either edition of LyteNyte Grid. If you have a license, install PRO. You can use PRO without a license, but the page will show a watermark. :::info If you do not have a React project yet, we recommend using Vite. Create a project quickly with: For details, see the Vite getting started docs. :::","link":"docs/intro-getting-started#installing-lytenyte-grid","depth":3},{"header":"Import LyteNyte Grid","text":"Import LyteNyte Grid LyteNyte Grid uses a modular design to minimize bundle size. The library exposes named exports to maximize tree-shaking. Grid is the main export. Grid is a React component, and it includes additional component functions attached to it. In this guide, you only need the Grid function. See the Headless Component Parts guide for advanced usage of the different component parts that make up LyteNyte Grid. Start by: Importing the Grid function.Defining the grid columns. The code below shows each part. The rest of this guide builds on this code, so if you are following along in your editor, copy it. The annotated lines mark important parts: Defines the set of column definitions. The grid uses these definitions to display content. To learn more about the available properties, see the Column Overview guide.Creates a 400px-tall container for the grid. LyteNyte Grid virtualizes rows by default, so the grid needs a container with a height to fill. See the Responsive Container guide for different approaches to creating a grid container. The container also applies the ln-grid class. LyteNyte Grid requires the ln-grid class for the pre-made styles. To learn more about styling, see the Grid Theming guide.Renders the grid. This example uses the default Grid component API. You can also use LyteNyte Grid in a headless fashion by providing children. To learn more about headless usage, see the Headless Component Parts guide.","link":"docs/intro-getting-started#import-lytenyte-grid","depth":3},{"header":"Providing Data to the Grid","text":"Providing Data to the Grid LyteNyte Grid reads data from a row data source. The most common option is a client-side data source. Use the client-side data source when all row data is available in the browser. Next you will: Import the row data and the data source hook.Create a row data source from the imported data and pass it to the grid. The annotated code marks the important changes. Import the useClientDataSource hook from the LyteNyte package. Use the client-side data source hook when the browser already has all row data.Import requestData from ./data.js. This guide assumes you downloaded the data for the demo and created a data.js (or data.ts) file. You can download or copy the data file from the LyteNyte Grid GitHub repository here. Create a local data.js file in the same folder as the file that renders the grid.Create a GridSpec type. LyteNyte Grid uses TypeScript for code completion and type checking. The grid cannot know the row data type ahead of time, so you define a specification interface and pass it as the type parameter to the relevant grid types. The specification interface can do more than define the row data type, as will be covered in a later section of this guide. For best practices on using TypeScript with LyteNyte Grid, see the TypeScript guide.Call the useClientDataSource hook and pass in requestData. Then set the grid's rowSource property to the ds value returned from useClientDataSource. If you followed along to this point you will have a working grid, but a very plain looking view. The demo below shows what the result looks like. The example looks plain, and not every column displays a value. LyteNyte Grid's default cell renderer only displays simple values, such as strings or numbers. For complex values, you must provide a custom cell renderer, as will be demonstrated next. ::demo[Basic Grid=\"./demos/getting-started_functional\"]","link":"docs/intro-getting-started#providing-data-to-the-grid","depth":3},{"header":"Custom Cell Renderer","text":"Custom Cell Renderer A custom cell renderer is a normal React component. LyteNyte Grid passes the cell renderer cell-specific properties, defined by the CellRendererParams type. You set a cell renderer on the column definitions you pass to the grid. Start by defining a cell renderer for the Timing Phase column. The code block below collapses the implementation, because a cell renderer can contain any React content you want. Focus on the function definition. You define a cell renderer by creating a React component that accepts CellRendererParams. This example also uses the GridSpec type, defined earlier, to improve type checking. Now that you know how to define cell renderers, set the cellRenderer property on the columns. The updated column code is shown below. :::note We use Tailwind CSS to style components. LyteNyte Grid has no opinion on which styling framework you use. If you follow this guide line by line and want the cell renderers to render correctly, set up Tailwind by following the Tailwind installation guide and apply our Tailwind theme configuration. The cell renderers also use custom CSS properties for colors. The CSS below defines those properties: For general theming and best practices, see the Grid Theming guide. ::: The cell renderers live in the components.js file. You can create this file yourself or copy our implementation from GitHub. A full working example is shown below. ::demo[Cell Renderers=\"./demos/getting-started_cell-renderers\"] The grid now looks much better, but you can still add more features. In the sections that follow, you will: Add the ability to sort columns. You will do this by extending the column definition with a custom sort property.Make every row a master-detail row that expands to reveal more information about the row data. All the features in these sections work in both the Core and PRO editions of LyteNyte Grid, so you can follow along regardless of which edition you use.","link":"docs/intro-getting-started#custom-cell-renderer","depth":2},{"header":"Column Sorting","text":"Column Sorting LyteNyte Grid can sort rows by a specific column. To enable sorting, you will: Extend the column definition with a sort property.Store the columns in React state, so you can update them through the LyteNyte Grid API.Provide a custom header renderer that sorts by a column when the user clicks the header.","link":"docs/intro-getting-started#column-sorting","depth":2},{"header":"Extending Columns","text":"Extending Columns Start by extending the column definition in the GridSpec interface, as shown below: This change only affects TypeScript. If you use JavaScript, you can safely ignore this section.","link":"docs/intro-getting-started#extending-columns","depth":3},{"header":"Controlled Column State","text":"Controlled Column State Next, store the columns in React state. The simplest way to do this is with useState, as shown in the code below: This code passes the state-backed columns to the grid, additionally it passes the setColumns state setter to the onColumnsChange prop. This lets the grid update column state when the user interacts with the grid.","link":"docs/intro-getting-started#controlled-column-state","depth":3},{"header":"Header Renderer","text":"Header Renderer With the column definitions in place, create a header renderer that sorts a column when the user clicks it. You can set a header renderer on a column with the headerRenderer property. Since all columns use the same header renderer, set headerRenderer on the base column through the grid's columnBase property. Next, define the header renderer and use it to update column state. The code below uses an onClick handler to build a column update by cycling through sort states for the clicked column. The handler then calls api.columnUpdate, which produces an updated set of columns. When api.columnUpdate runs, the grid calls onColumnsChange with the new columns. Finally, pass a sort model to the client row data source. Create the sort model by deriving the value from the column state. The code below derives a sort model from the columns which have a sort value set on their specification. A full working example with column sorting is shown below. ::demo[Sorting Demo=\"./demos/getting-started-with-sort\"]","link":"docs/intro-getting-started#header-renderer","depth":3},{"header":"Row Master Detail","text":"Row Master Detail LyteNyte Grid supports row detail information, also known as master detail. This section covers the basic setup and functionality. For more information, see the Row Master Detail guide. To enable row master detail, you will: Define a detail renderer.Enable LyteNyte Grid's marker column and provide a cell renderer that toggles the row detail expansion state.","link":"docs/intro-getting-started#row-master-detail","depth":2},{"header":"Row Detail Renderer","text":"Row Detail Renderer A row detail renderer is a React component that LyteNyte Grid uses to render the detail area for an expanded row. Set the renderer on the grid's rowDetailRenderer property, as shown below: The renderer is a normal React component that receives row props from LyteNyte Grid. The definition of a detail renderer is shown below. The content is collapsed because the important line is the function definition. A row detail renderer can return any React content. The key requirements are that the function returns a valid ReactNode and accepts RowParams as a prop.","link":"docs/intro-getting-started#row-detail-renderer","depth":3},{"header":"Marker Column","text":"Marker Column After you define the row detail renderer, you need a way to toggle the visibility of each detail row. LyteNyte Grid's marker column provides the perfect place to add this functionality. The marker column is a special column that LyteNyte Grid creates and manages. LyteNyte always pins the marker column to the start of the grid, and the marker column does not appear in the columns array you pass to the grid. Use the marker column for auxiliary row actions such as row selection or row detail expansion. To enable the marker column, set the grid's columnMarker property to a marker column definition, as shown below: The marker column uses the MarkerCell component as its cell renderer. The MarkerCell definition is shown below. Focus on the api.rowDetailToggle method, which toggles the row detail expansion state for a given row. Use this method to show and hide row detail areas. Putting everything together, the full working example is shown below: ::demo[Complete Getting Started Demo=\"./demos/getting-started\"]","link":"docs/intro-getting-started#marker-column","depth":3},{"header":"GitHub","text":"GitHub The primary support channel for LyteNyte Grid is our public GitHub repository. Here we track: Bug reportsFeature requestsDocumentation feedbackGeneral questions When raising a support request, please select the appropriate issue template on GitHub. Each template includes specific guidance to help you provide the information we need to assist you effectively. Our development team actively monitors all submissions. We aim to respond to each issue, but response times may vary as priority is given to LyteNyte Grid PRO license holders, who are guaranteed responses under their support agreements. :::info PRO users receive 12 months of dedicated technical support with each purchase or renewal of their LyteNyte Grid license plan. Active PRO license holders should include their customer ID and license plan when raising a GitHub issue to enable priority handling and a guaranteed 24-hour response. :::","link":"docs/intro-getting-support#github","depth":2},{"header":"Email Support","text":"Email Support Email support is a dedicated channel for active PRO license holders who need confidential technical assistance, especially for issues not suited to the public GitHub repository. Contact us at support@1771technologies.com and include your company name, customer ID, and license plan to receive priority handling.","link":"docs/intro-getting-support#email-support","depth":2},{"header":"Client Portal","text":"Client Portal PRO users gain exclusive access to the 1771 client portal. In the portal, they can: Manage licenses by reviewing, updating, and staying on top of license details.Filter GitHub issues raised by their team for easier tracking.Submit confidential issues not suited to public GitHub reporting.","link":"docs/intro-getting-support#client-portal","depth":2},{"header":"Social Media","text":"Social Media Follow us on our social platforms for updates, tutorials, and community engagement: LinkedInX (Twitter)YouTubeInstagram","link":"docs/intro-getting-support#social-media","depth":3},{"header":"Reddit Communities","text":"Reddit Communities We are active in the following development-focused subreddits, where you can ask questions to the community and our team: r/reactjsr/reactr/typescriptr/Frontend","link":"docs/intro-getting-support#reddit-communities","depth":3},{"header":"Additional Resources","text":"Additional Resources For support policy details, see the Support page and EULA. To learn more about our support guiding principles, read Support the 1771 Technologies Way.","link":"docs/intro-getting-support#additional-resources","depth":2},{"header":"Before You Start","text":"Before You Start Before adding LyteNyte Grid components, ensure your project is set up with shadcn. Follow the shadcn/ui installation guide for your framework to get started.","link":"docs/intro-installation-shadcn#before-you-start","depth":2},{"header":"The LyteNyte Registry","text":"The LyteNyte Registry LyteNyte Grid components are available under the @lytenyte namespace. The shadcn CLI automatically locates the 1771 Technologies public registry. Run the following command to get started: If you're using the PRO edition of LyteNyte Grid, run the following instead: After you run the command, shadcn adds a lytenyte-core.tsx or lytenyte-pro.tsx component file, along with a corresponding hook file (use-lytenyte-core.tsx or use-lytenyte-pro.tsx). Each component file includes the preconfigured, headless LyteNyte Grid parts for your project. Modify these as needed for your application. Depending on your import path configuration, importing the components is as simple as:","link":"docs/intro-installation-shadcn#the-lytenyte-registry","depth":2},{"header":"Manual Installation","text":"Manual Installation Add @1771technologies/lytenyte-core or @1771technologies/lytenyte-pro to your project dependencies: Next copy and paste the following code into your project:","link":"docs/intro-installation-shadcn#manual-installation","depth":2},{"header":"Upgrading to PRO","text":"Upgrading to PRO LyteNyte Grid PRO extends LyteNyte Grid Core. If you start with LyteNyte Grid Core and later upgrade to PRO, you only need to update the package imports. Simply replace: With:","link":"docs/intro-installation#upgrading-to-pro","depth":2},{"header":"Trial Licensing","text":"Trial Licensing The PRO edition requires a valid Developer PRO or Organization PRO license for production. You can install and evaluate it without a license. The grid will remain fully functional, but a persistent watermark will appear in both development and production. To remove the watermark, activate your license key as explained in the License Activation guide. For details on licensing options and pricing, visit our pricing page.","link":"docs/intro-installation#trial-licensing","depth":2},{"header":"CDN Installation","text":"CDN Installation LyteNyte Grid is available via CDN through NPM's distribution network. While the package offers various entry points depending on your component requirements, the following CDN links provide access to all available code files: unpkgJsDelivr","link":"docs/intro-installation#cdn-installation","depth":2},{"header":"Source Installation","text":"Source Installation The complete source code for LyteNyte Grid is publicly available on GitHub. You can clone and build the project directly from source if your project requires customization. Important: When building from source, you must adhere to the respective licenses for both editions. Using or developing code from the PRO source requires a valid license to be purchased. Please review the LyteNyte Grid End User License Agreement for complete legal terms.","link":"docs/intro-installation#source-installation","depth":2},{"header":"Activation","text":"Activation Activate your license by calling the activateLicense function before using LyteNyte Grid in your web application: The activateLicense function is lightweight and can be called many times. If your application has multiple potential entry points, calling activateLicense for each entry point is perfectly fine.","link":"docs/intro-license-activation#activation","depth":2},{"header":"Validation Process","text":"Validation Process We validate your license key to ensure compliance with the LyteNyte Grid PRO End User License Agreement (EULA). Although each developer active in the front-end code must be licensed, the key only needs to be set once per project where LyteNyte Grid is used. The activateLicense function validates your license key using the encoded information and the internal build date of the LyteNyte Grid code. This validation happens completely offline without any external network requests.","link":"docs/intro-license-activation#validation-process","depth":2},{"header":"Validation Failures","text":"Validation Failures If your license key is missing, invalid, or expired, LyteNyte Grid shows a notification warning indicating that a valid license key is required. If you see a watermark despite having a valid license key or cannot retrieve your license key, please get in touch with us at account-help@1771technologies.com. Here are the various possible validation errors:","link":"docs/intro-license-activation#validation-failures","depth":2},{"header":"Missing License Key","text":"Missing License Key “LyteNyte Grid PRO is being used for evaluation. Click here to secure your license.” This notification appears when no license key has been set, or when evaluating LyteNyte Grid PRO without a valid license.","link":"docs/intro-license-activation#missing-license-key","depth":3},{"header":"Invalid License Key","text":"Invalid License Key “Invalid license key. Please verify the key and try again.” This notification indicates that your PRO license key is invalid. It could be due to a typo or a missing character in the key. To resolve this issue, verify that your license key is entered correctly. For detailed steps, refer to the Installation guide.","link":"docs/intro-license-activation#invalid-license-key","depth":3},{"header":"Expired License Key","text":"Expired License Key “License key expired. Your license covers earlier versions only.” This notification indicates that your PRO license key is valid, but it doesn't cover the version of LyteNyte Grid PRO you're using. PRO licenses are perpetual but only apply to versions released during your active license term. To use this version, you'll need to renew your license.","link":"docs/intro-license-activation#expired-license-key","depth":3},{"header":"Accessibility Compliance","text":"Accessibility Compliance LyteNyte Grid complies with WCAG 2.0 guidelines at levels A, AA, and AAA. Level AA compliance typically satisfies ADA standards. The grid uses ARIA (Accessible Rich Internet Applications) attributes on rows, cells, and headers. Screen readers interpret these attributes to help visually impaired users understand the grid's structure and content.","link":"docs/accessibility#accessibility-compliance","depth":2},{"header":"DOM Ordering","text":"DOM Ordering LyteNyte Grid renders cells and headers in natural DOM order, even when virtualization is enabled. This ensures that screen reader users can correctly parse content. No special configuration is required to enhance the grid's accessibility.","link":"docs/accessibility#dom-ordering","depth":2},{"header":"ARIA Roles and Properties","text":"ARIA Roles and Properties LyteNyte Grid applies ARIA roles and properties to remain spec-compliant and accessible.","link":"docs/accessibility#aria-roles-and-properties","depth":2},{"header":"Grid Roles","text":"Grid Roles The main container uses role=\"grid\". It also applies: aria-rowcount: total rows in the grid.aria-colcount: total columns in the grid.aria-multiselectable=\"true\": only when multiple row selection is enabled.","link":"docs/accessibility#grid-roles","depth":3},{"header":"Row Roles","text":"Row Roles The grid uses role=\"row\" for individual rows and role=\"rowgroup\" for row containers. For role=\"row\", the grid adds: aria-rowindex: the row's position in the grid.aria-selected: whether the row is selected.","link":"docs/accessibility#row-roles","depth":3},{"header":"Header Cell Roles","text":"Header Cell Roles Headers use role=\"columnheader\" and may include: aria-colindex: the column's position in the grid.aria-expanded: open or collapsed state.","link":"docs/accessibility#header-cell-roles","depth":3},{"header":"Grid Cell Roles","text":"Grid Cell Roles Cells use role=\"gridcell\" and may include: aria-colindex: the column's position in the grid.","link":"docs/accessibility#grid-cell-roles","depth":3},{"header":"Screen Readers","text":"Screen Readers LyteNyte Grid works with major screen readers, including: JAWSVoiceOver The grid follows W3C standards to ensure a smooth, predictable reading experience.","link":"docs/accessibility#screen-readers","depth":3},{"header":"Color Accessibility","text":"Color Accessibility Roughly 300 million people worldwide experience some form of color blindness (source). Use accessible colors to meet AA standards and maintain readability. LyteNyte Grid provides prebuilt grid themes and color design tokens. These themes are designed to be accessible and spec-compliant. If you don't use a built-in LyteNyte Grid theme and instead customize your own, choose a high-contrast palette to maintain accessibility. Recommended palettes include: Radix UI ColorsTailwind Colors Accessible colors ensure data remains legible for everyone. LyteNyte Grid supports accessible implementations. Use correct ARIA roles, test with screen readers, and validate your color choices. Reassess with assistive technology whenever you update the UI.","link":"docs/accessibility#color-accessibility","depth":3},{"header":"Navigation","text":"Navigation Use the arrow keys (←, →, ↑, ↓) to move between header and grid cells. The ← and → arrow keys cycle through all tabbable elements within a cell before moving to the next cell. This behavior differs slightly from the ARIA Grid Pattern's recommended navigation model, but it provides a more efficient navigation experience. You can combine the arrow keys with your keyboard's meta modifier to navigate to the bounds of the grid: Ctrl →: Jump to the last cell in the current row or header.Ctrl ←: Jump to the first cell in the current row or header.Ctrl ↑: Focus the first row (when a grid cell is focused).Ctrl ↓: Focus the last row (when a grid cell is focused). :::note On macOS, you can use the Cmd key instead of the Ctrl key. ::: In addition to the arrow keys, LyteNyte Grid supports the following navigation keybindings: PageDown: Move focus down by approximately the number of rows currently in view.PageUp: Move focus up by approximately the number of rows currently in view.Home: Move focus to the first cell of the current row, equivalent to Ctrl ←.End: Move focus to the last cell of the current row, equivalent to Ctrl →.Ctrl Home: Move focus to the first cell in the first row.Ctrl End: Move focus to the last cell in the last row.","link":"docs/keyboard#navigation","depth":2},{"header":"Tab Order","text":"Tab Order LyteNyte Grid orders DOM elements to match the visual layout and sets the viewport and cells to tabIndex=0. With this configuration, Tab would normally follow the DOM order. However, the grid treats the viewport as a single tab stop, except during cell editing. Pressing Tab moves focus to the next element outside the grid, so users don't tab through every cell before reaching the next focus group.","link":"docs/keyboard#tab-order","depth":3},{"header":"Row Detail Navigation","text":"Row Detail Navigation Navigate expanded row details using the keyboard. When a cell is focused and the row is expanded, press ↓ to focus the detail container. Press ↓ again to move to the next row. The demo supports all navigation bindings. Use it to explore the grid's response to inputs. ::demo[Row Detail Navigation=\"../(introduction)/demos/getting-started\"]","link":"docs/keyboard#row-detail-navigation","depth":3},{"header":"Cell Selection","text":"Cell Selection When cellSelectionMode is set to range, you can use the following keybindings to modify the selection. See the Cell Range Selection guide for detailed guidance. Shift →: Expand or shrink the selection one column to the right.Shift ←: Expand or shrink the selection one column to the left.Shift ↑: Expand or shrink the selection one row up.Shift ↓: Expand or shrink the selection one row down.Ctrl Shift →: Expand or shrink the selection to the last column on the right.Ctrl Shift ←: Expand or shrink the selection to the last column on the left.Ctrl Shift ↑: Expand or shrink the selection to the first row.Ctrl Shift ↓: Expand or shrink the selection to the last row.Ctrl A: Select all cells in the grid. Selection expansion depends on the current selection rectangle and the pivot cell, which is the focused cell. Use the demo below to explore selection bindings. ::demo[Cell Selection Navigation=\"../(cells)/demos/cell-selection\"]","link":"docs/keyboard#cell-selection","depth":2},{"header":"Editable Cells","text":"Editable Cells When cell editing is enabled, LyteNyte Grid adds additional keybindings to make keyboard-based editing intuitive. See the Cell Editing guide for details on updating cell values. Press Enter to enter edit mode.Press Enter again to confirm and commit the edit.Press Esc to cancel the edit.Press any printable character to enter edit mode and set the cell value to the pressed character.","link":"docs/keyboard#editable-cells","depth":2},{"header":"Key Binding Customization","text":"Key Binding Customization LyteNyte Grid is headless, so you can add custom keybindings by handling the component's onKeyDown. The demo below binds Space to row selection. Double-click a row to select it, or focus a cell and press Space. For more details, see the Row Selection guide. ::demo[Row Selection Keybindings=\"./demos/row-selection-space\"] Use the events property to attach custom handlers without switching to a fully headless implementation. The example below adds a custom keybinding using the grid's events property. If you use the fully headless grid setup, the equivalent implementation is shown below. See the Headless Component Parts guide for more details.","link":"docs/keyboard#key-binding-customization","depth":2},{"header":"Overwriting Existing Bindings","text":"Overwriting Existing Bindings LyteNyte Grid registers all default keybindings on the Grid.Viewport element. If you provide a handler that prevents the default behavior or stops event propagation, LyteNyte Grid will not activate its built-in keybindings. The example below prevents all default grid keybindings. Modify this pattern carefully to match your application's requirements. :::warn Overriding or disabling LyteNyte Grid's default keybindings is not recommended. These keybindings are designed to maximize accessibility. Changing or preventing them will reduce the overall accessibility of your application. :::","link":"docs/keyboard#overwriting-existing-bindings","depth":3},{"header":"Touch Interactions","text":"Touch Interactions LyteNyte Grid supports touch devices. Browser click events fire on tap, and the grid uses these events for its interaction handlers.","link":"docs/keyboard#touch-interactions","depth":2},{"header":"Filtering Paginated Rows","text":"Filtering Paginated Rows To filter rows on the server, define a filter model and include it in each data request. Include the filter model in the queryKey of the useServerDataSource hook. Whenever the filter model changes, the grid fetches a new set of rows from the server. The following demo passes a custom filter model to the queryKey of the server data source. Click the funnel icon in a column header to apply a filter. ::demo[Row Pagination Filtering=\"./demos/server-filtering\"] For more guidance on filtering rows on the server, see the Server Row Filtering guide.","link":"docs/paginated-rs-row-filtering#filtering-paginated-rows","depth":2},{"header":"Sorting Paginated Rows","text":"Sorting Paginated Rows To sort rows on the server, define a sort model and send it with each request. The useServerDataSource hook accepts the sort model as part of the queryKey. The sort model can use any structure that fits your application. In most cases, match the format your server or database expects. The demo below shows paginated row sorting. Click a column header to update that column's sort state. ::demo[Row Pagination Sorting=\"./demos/server-sorting\"] The demo code applies sort state to individual columns. By extending the grid's API, you can update the sort property on each column directly. The extended API then applies the appropriate sort when the user clicks a column header.","link":"docs/paginated-rs-row-sorting#sorting-paginated-rows","depth":2},{"header":"Server-Side Row Pagination","text":"Server-Side Row Pagination Paginate rows by combining the server data source with a page cursor. Set up pagination in three steps: Define a page value in React state, for example using the useState hook.Include the page value in the queryKey property of the server data source.Pass a queryFn callback that loads rows for the current page. When the page value changes, LyteNyte Grid calls queryFn to fetch the next page of rows. The demo below shows basic pagination with caching, so previously loaded pages render without refetching when you return to them. ::demo[Row Pagination=\"./demos/basic-pagination\"] The server returns both the current page's data and the total row count, allowing you to calculate the total number of available pages.","link":"docs/paginated-rs-rows#server-side-row-pagination","depth":2},{"header":"Pagination Row Size","text":"Pagination Row Size Configure the rows per page by sending pageSize to the server with the current page value. Including pageSize in the server data source's queryKey ensures that any change in page size resets the grid view and triggers a new server fetch. In the demo below, use the Row per page menu to change the page size. Changing the page size clears the response cache and resets the page number to one. ::demo[Rows Per Page=\"./demos/pagination-change-size\"]","link":"docs/paginated-rs-rows#pagination-row-size","depth":2},{"header":"Pivot Columns","text":"Pivot Columns Set the columns property on the pivotModel of the client data source to configure which columns are used to dynamically generate pivot columns. The values in the columns property are used to create the pivot column definitions. The demo below lets you select pivot columns. Try different column combinations to see how pivoting behaves. ::demo[Column Pivots=\"./demos/pivot-columns\"] Each pivot you add deepens the column hierarchy, and the number of generated columns can grow exponentially. LyteNyte Grid imposes no limits on the number of pivots you can apply, so your application should limit this based on your data requirements. Without pivot rows, LyteNyte Grid aggregates all rows into a single totals row. See the Pivot Rows section of this guide to add pivot rows.","link":"docs/pivoting-columns-and-rows#pivot-columns","depth":2},{"header":"Pivot Column Group Expansions","text":"Pivot Column Group Expansions Defining multiple pivot columns creates a column group hierarchy. Since the grid generates these dynamically, they are not explicitly defined in your application state. The client data source manages column group expansion state internally. You can collapse column groups using the grid's api.columnToggleGroup method. The demo below shows this behavior. The grid is pivoted on Age Group and Gender. A custom header group renderer allows column groups to be collapsed. Notice that the Total columns remain visible even when the group is collapsed. ::demo[Pivot Column Groups=\"./demos/pivot-columns-expansions\"]","link":"docs/pivoting-columns-and-rows#pivot-column-group-expansions","depth":3},{"header":"Pivot Column Updates","text":"Pivot Column Updates The client data source dynamically generates pivot columns and manages their column state. This lets you move and resize these generated pivot columns as shown in the demo below. ::demo[Update Pivot Columns=\"./demos/pivot-columns-state\"]","link":"docs/pivoting-columns-and-rows#pivot-column-updates","depth":3},{"header":"Pivot Column Modification","text":"Pivot Column Modification LyteNyte Grid dynamically creates pivot columns based on the provided column configuration. Set the pivotColumnProcessor property on the useClientDataSource hook to modify these columns before they are rendered. Using pivotColumnProcessor, you can modify dynamically created columns in any way that suits your application. You can reorder or remove columns, or change any column property. The demo below uses pivotColumnProcessor to remove the Total columns created by the column pivots. ::demo[Modifying Pivot Columns=\"./demos/pivot-columns-process\"] The code for the pivot column processor is shown below. The processor returns a new array of columns based on the columns generated by the grid.","link":"docs/pivoting-columns-and-rows#pivot-column-modification","depth":3},{"header":"Pivot Rows","text":"Pivot Rows Add pivot row groups to extend the pivot view by another dimension. Set the rows property on the pivotModel to group rows before measurement. Note that, unlike standard row grouping, the final level of a pivot row group represents an aggregated result and cannot be expanded. The demo below shows pivot rows. Each pivot row adds a grouping level. Since pivoting rotates data dimensions, row and column definitions are interchangeable. Click the Swap button to invert the current configuration. ::demo[Row Pivots=\"./demos/pivot-rows\"] The demo uses the Pill Manager component to configure pivots. React state stores the pivot configuration and updates it when pills are clicked or dragged. The updated pivot state is then mapped to the client data source, as shown in the code below.","link":"docs/pivoting-columns-and-rows#pivot-rows","depth":2},{"header":"Filtering Pivots","text":"Filtering Pivots Pivoting data always creates group rows because pivots aggregate data. To apply filters to pivoted data, set the filter property on the pivot model. The filter property accepts an array of HavingFilterFn functions. The demo below demonstrates having filter functions. You can update the filter applied to the pivot column by clicking the funnel icon in the column header. Having filters are applied to rows at a specific depth; in this case, the child rows of countries will be filtered. Refer to the Client Row Having Filters guide for more details. ::demo[Filtering Pivots=\"./demos/pivot-filtering\"] The API extends the grid with a filter model. This model's state is converted into a filter function and applied to the client data source's pivot model, as shown below.","link":"docs/pivoting-filtering#filtering-pivots","depth":2},{"header":"Pre-Pivot Row Filtering","text":"Pre-Pivot Row Filtering By default, LyteNyte Grid does not apply standard row filters when pivoting and uses all rows to create pivot columns. To apply standard row filters before pivoting, set pivotApplyExistingFilter. The grid filters rows first, then generates pivoted data. In the demo below, the grid filters rows to include only entries where the Country is the United States or Germany. For row filtering details, see the Client Row Filtering guide. ::demo[Filter Before Pivot=\"./demos/pivot-existing-filters\"]","link":"docs/pivoting-filtering#pre-pivot-row-filtering","depth":3},{"header":"Label Filters","text":"Label Filters Pivoting creates labels for rows and columns. Use rowLabelFilter and colLabelFilter to filter pivot results by row and column labels. Label filters allow you to include or exclude specific identifiers from the pivot output. For details, see the Client Row Label Filters guide. The demo below applies two label filters. The row label filter keeps only the United States and Germany. The column label filter excludes Youth (<25) age group. ::demo[Pivot Label Filters=\"./demos/pivot-label-filters\"] Label filters are always applied as an array. Each item in the array filters labels at the depth corresponding to its index. This means the first item filters the top-level labels, the second item filters the next level, and so on. The code below shows the label filters used in the demo.","link":"docs/pivoting-filtering#label-filters","depth":2},{"header":"Grand Total Row","text":"Grand Total Row To display the grand totals row for pivots, set the pivotGrandTotals property on the client row data source. The pivotGrandTotals property accepts one of two values: \"top\": Pins the grand totals row to the top of the viewport.\"bottom\": Pins the grand totals row to the bottom of the viewport. The grand totals row is an aggregation row. Aggregation rows summarize data but do not contain any children. In the case of pivots, the grand totals row aggregates all rows in the current view. The demo below shows a measure summing Profit. Rows are pivoted by Country and Product, columns by Age Group. The grand totals row is positioned at the bottom of the viewport. ::demo[Row Grand Totals=\"./demos/pivot-grand-totals\"] The code below shows the client data source configuration that enables the grand totals row. To compute the grand totals row aggregations, the grid uses the measures defined in the pivotModel property.","link":"docs/pivoting-grand-totals#grand-total-row","depth":2},{"header":"Defining Measures","text":"Defining Measures Define a measure by setting the measure property on the pivot model. LyteNyte Grid uses a measure for two purposes: Aggregate row data based on the row pivot and column pivot dimensions.If the measure dimension is a column, use it as a reference for the columns created from the pivot configuration. The demo below shows a measure that sums Profit. Rows are pivoted by Country and columns by Age Group, creating a profit breakdown per country and age demographic. ::demo[Sum of Profit Measure=\"./demos/pivot-measures\"] The code below shows how to apply the measure. This demo uses a predefined measure function called \"sum\", but you can also provide a function inline if that better suits your application's use case.","link":"docs/pivoting-measures#defining-measures","depth":2},{"header":"Configuring Multiple Measures","text":"Configuring Multiple Measures You can apply multiple measures to the pivot configuration by providing additional dimensions. The order of measures does not affect aggregation behavior, but it does influence the initial column order when pivots are applied. The demo below demonstrates multiple measures in action. You can also update the measure function by clicking the function name in the pill and selecting a different measure for that column. ::demo[Multiple Measures=\"./demos/pivot-measures-updates\"] Be aware that measures multiply your column count. If a pivot generates 20 columns, two measures produce 40 columns, and three produce 60.","link":"docs/pivoting-measures#configuring-multiple-measures","depth":3},{"header":"Multiple Measures Per Column","text":"Multiple Measures Per Column You can measure the same column multiple times to apply different measure functions. The demo below measures Profit with both sum and avg. The grid treats each measure as distinct, even when both reference the same column. ::demo[Column Multiple Measures=\"./demos/pivot-measures-multiple\"] LyteNyte Grid identifies measures by id. When you apply multiple measures to the same column, you must provide a unique ID for each measure. The example below shows how to configure this:","link":"docs/pivoting-measures#multiple-measures-per-column","depth":2},{"header":"Pivot Reference Implementation","text":"Pivot Reference Implementation The demo below shows a complete, working pivoting example that you can use as a reference implementation. The remaining guides in the pivoting section focus on specific parts of the pivot configuration that LyteNyte Grid offers. ::demo[Complete Pivot Implementation=\"./demos/pivot-full\"] The remainder of this section explains how to enable basic pivots and provides the foundational knowledge required to understand the other guides in the pivoting section.","link":"docs/pivoting-overview#pivot-reference-implementation","depth":2},{"header":"Pivot Mode","text":"Pivot Mode LyteNyte Grid does not maintain pivot state internally. It renders only the props provided, delegating pivot state management to the client row data source. To implement pivoting: Set the pivotMode property to true on the client data source.Provide a pivotModel value.Call the usePivotProps hook on the client data source and pass the returned props to the grid. The code below shows the minimum configuration required. Use the toggle below to enable pivot mode. The grid automatically pivots the Age Group values, groups rows by Country, and measures total Profit. ::demo[Basic Pivoting=\"./demos/pivot-basic\"] Rows are grouped by Country. Since pivoting presents data in a strictly aggregated state, these groups cannot be expanded to reveal individual sub-rows. The code below shows the demo's pivot configuration. The grid uses the measures definition as a template for pivot-generated columns. In this example, Profit provides the base column definition for the generated pivot columns, and a header override ensures the new columns display the corresponding Age Group values.","link":"docs/pivoting-overview#pivot-mode","depth":2},{"header":"Client Source Pivot Model","text":"Client Source Pivot Model The pivotModel property of the useClientDataSource hook defines how LyteNyte Grid renders pivoted rows. The interface is shown below. All properties in the pivot model are optional. The columns, rows, and measures properties determine the structure of the pivot view. Their behavior is as follows: If you provide no values for columns, rows, or measures, the pivot view will be blank.If columns contains one or more entries, LyteNyte Grid uses the cell values from those columns to generate dynamic pivot columns.If rows contains one or more entries, LyteNyte Grid groups the measure output by the specified rows.If measures is provided, LyteNyte Grid aggregates grid rows using the defined measures. Measure behavior depends on which other properties are present:If only measures are provided, LyteNyte Grid creates one column per measure and aggregates across all rows, producing a single totals row.If measures and columns are provided, LyteNyte Grid aggregates measures for each dynamically generated pivot column.If measures and rows are provided, LyteNyte Grid aggregates measures for each row group.If measures, rows, and columns are all provided, LyteNyte Grid aggregates measures for each row group, split across the dynamically generated pivot columns.","link":"docs/pivoting-overview#client-source-pivot-model","depth":3},{"header":"Sorting Pivots","text":"Sorting Pivots To sort pivot columns, set the sort property on the pivot model. In the demo below, click a column header to sort by that pivot column. The demo uses a measure that sums Profit. ::demo[Pivot Sorting=\"./demos/pivot-sorting\"] The demo applies the sort configuration to the pivot model state. As a result, rows are sorted based on the generated pivot columns. If the pivot columns change, the sort is removed because the sorted field no longer exists. LyteNyte Grid generates IDs for pivot columns based on pivot data. For example, in the demo above, the ID of the 25-34 column is \"25-34-->profit\". The first segment represents the cell value from the Age Group column, and the second segment represents the ID of the applied measure, in this case Profit. Use this generated sort ID when defining a sort function for pivoted rows. If you need predictable column IDs, use the pivotColumnProcessor property on the client data source to modify column IDs before the pivot columns are returned.","link":"docs/pivoting-sorting#sorting-pivots","depth":2},{"header":"Controlled Pivot State","text":"Controlled Pivot State Set pivotState on the client data source to manage pivot state. To keep it in sync, handle onPivotStateChange and write the updated state back to pivotState. The demo below shows a measure summing Profit, with rows pivoted by Country and Product, and columns by Age Group. It demonstrates controlled pivot state. This configuration behaves identically to the default (uncontrolled) approach, but the state is now exposed to your application. ::demo[Controlled Pivot State=\"./demos/pivot-persistence\"] The code to control the pivot state is shown below. The initial state starts with the Australia row group collapsed. To control pivot state, the onPivotStateChange is set to the setPivotState function returned by React's useState hook. :::warn Only control pivot state when your use case requires it. Do not use controlled pivot state to modify columns generated by the client data source. To adjust generated columns, use pivotColumnProcessor instead. :::","link":"docs/pivoting-state-persistence#controlled-pivot-state","depth":2},{"header":"Tree Shaking","text":"Tree Shaking Tree shaking removes unused JavaScript code from your final bundle. The example below shows a minimal LyteNyte Grid setup. Although the useServerDataSource hook is imported, the component never uses it. Modern bundlers detect unused imports and exclude their code from the bundle. For a bundler to tree shake correctly, your code must meet several criteria. It must be statically analyzable, meaning the bundler can determine the structure of imports at compile time. The code must also be side-effect free. Importing a module should not execute hidden logic. Finally, modules must be structured so that unused pieces can be removed without affecting other parts of the code. LyteNyte Grid fulfills all of these criteria. Tree shaking occurs automatically in bundlers like Next.js and Vite, and LyteNyte Grid is designed to work seamlessly with them. :::info Other data grids use dynamic dispatch systems to include or exclude features. If you are switching from those tools, you might expect similar mechanisms. LyteNyte Grid avoids them by design. Requiring users to manually specify which code to include leads to unnecessary configuration, especially when modern ESM JavaScript supports static analysis. LyteNyte Grid's headless architecture lets bundlers determine which parts of the library you use and safely remove the rest. This keeps your bundle size small. :::","link":"docs/prodready-bundling#tree-shaking","depth":2},{"header":"Choosing a Bundler","text":"Choosing a Bundler The LyteNyte Grid package ships as a set of ESM modules. Modern browsers can load ESM files directly, but bundling your modules into a single file, or a small set of files, remains best practice for performance and caching. You need a bundler to produce these optimized outputs. LyteNyte Grid works with any standards-compliant bundler. Popular options include: ViteRollupRspack","link":"docs/prodready-bundling#choosing-a-bundler","depth":2},{"header":"Bundling Best Practices","text":"Bundling Best Practices Follow these guidelines to ensure optimal bundling when using LyteNyte Grid: Use ES Modules: Favor ES module syntax (import and export) instead of CommonJS (require).Enable Tree Shaking: Verify that tree shaking is enabled in your bundler configuration.Avoid Side Effects: Ensure your code doesn't introduce side effects that could prevent tree shaking, such as modifying imported modules or using imported functions in ways that can't be statically analyzed.Use Specific Imports: Avoid import * as All from \"\" syntax as these wildcard imports can prevent most bundlers from effectively tree shaking. By applying these guidelines and leveraging LyteNyte Grid's architecture, you can build efficient applications with minimal bundle size. If you use a modern framework like Next.js, Remix, or Vite, these optimizations work automatically. Add LyteNyte Grid as a dependency and begin using the grid without additional configuration.","link":"docs/prodready-bundling#bundling-best-practices","depth":3},{"header":"Semantic Versioning","text":"Semantic Versioning All LyteNyte Grid packages follow semver versioning. Each version consists of three numbers (major.minor.patch), such as 1.2.0. Each number represents a different type of release: Major: Introduces breaking changes that require updates in your application.Minor: Adds new, backward-compatible features and works with your existing code.Patch: Includes bug fixes and small improvements. We refer to minor and patch releases as updates, while major releases are treated as upgrades.","link":"docs/prodready-grid-versioning#semantic-versioning","depth":2},{"header":"Release Cadence","text":"Release Cadence LyteNyte Grid does not follow a fixed release schedule. Bug fixes ship as soon as they pass internal testing. Although major releases may introduce significant new capabilities, we prioritize stability and aim to avoid breaking changes. Most enhancements are delivered as backward-compatible minor updates. When a release adds substantial new functionality, we may publish a new major version even if there are no breaking changes. Breaking changes are always documented in the changelog. While not every major release contains breaking changes, any breaking change will trigger a major version bump. This ensures your application remains stable when updating only minor versions.","link":"docs/prodready-grid-versioning#release-cadence","depth":2},{"header":"Checking for the Latest Version","text":"Checking for the Latest Version You can find the latest published version of LyteNyte Grid on the NPM Registry: LyteNyte Grid CoreLyteNyte Grid PRO","link":"docs/prodready-grid-versioning#checking-for-the-latest-version","depth":3},{"header":"Fixed Versioning","text":"Fixed Versioning LyteNyte Grid packages use a fixed versioning scheme. The code base is a monorepo, and all packages are released under the same version number. As a result, the LyteNyte Grid Core and LyteNyte Grid PRO packages always share the same version, even when a change affects only one package.","link":"docs/prodready-grid-versioning#fixed-versioning","depth":3},{"header":"LyteNyte Grid PRO Version Access","text":"LyteNyte Grid PRO Version Access When you purchase a LyteNyte Grid PRO license, you receive 12 months of dedicated technical support and access to the latest software versions. These 12 months are known as your license period. You may use any version of LyteNyte Grid PRO released within your active license period indefinitely. However, versions released after your license expires require a new license purchase or renewal. For more on licensing terms, see our Pricing page.","link":"docs/prodready-grid-versioning#lytenyte-grid-pro-version-access","depth":2},{"header":"Content Security Policy","text":"Content Security Policy LyteNyte Grid may require specific Content Security Policy (CSP) settings. If you need background on CSP, see the MDN article on Content Security Policy.","link":"docs/prodready-security#content-security-policy","depth":2},{"header":"Style Source CSP","text":"Style Source CSP To enable LyteNyte Grid's full feature set, the style-src directive in your CSP must include unsafe-inline. This directive supports advanced layout and virtualization features. Add the following tag to your HTML if your framework does not already provide it: :::info Add this tag only if you use a custom in-house meta framework. Popular frameworks such as Vite, React Router, TanStack Start, and Next.js already provide the necessary tags. ::: LyteNyte Grid requires unsafe-inline, but its implementation of inline styles is designed to minimize risk. Inline styles can contribute to XSS vulnerabilities when used improperly. LyteNyte Grid mitigates these risks through: Sanitization: All style definitions are validated before use to prevent malicious content.Controlled Execution Context: Inline styles are generated only within tightly controlled internal logic.Content Isolation: Dynamically created elements avoid sensitive parts of the DOM and prevent script execution.Framework Compatibility: Frameworks commonly paired with LyteNyte Grid include additional mechanisms that safely handle inline styles. These protections allow LyteNyte Grid to use inline styles without compromising your application's security. Many modern frameworks also include unsafe-inline in their CSP defaults for similar reasons.","link":"docs/prodready-security#style-source-csp","depth":3},{"header":"Secure Data Transportation","text":"Secure Data Transportation LyteNyte Grid runs entirely in the browser. It does not perform any server communication. Your application is responsible for retrieving and securing all data before passing it to the grid.","link":"docs/prodready-security#secure-data-transportation","depth":2},{"header":"Avoidance of Attack Vectors","text":"Avoidance of Attack Vectors LyteNyte Grid avoids JavaScript features commonly associated with security exploits: The code base never uses eval, and future versions of LyteNyte Grid will not introduce it.LyteNyte Grid does not modify the prototype chain of any object.The library has no external dependencies, ensuring that no third-party code is downloaded during installation. We actively monitor and work to keep our codebase free of vulnerabilities, continuously enhancing our defenses against new threats. If you discover a security issue, email support@1771technologies.com. Our team will address the issue promptly.","link":"docs/prodready-security#avoidance-of-attack-vectors","depth":2},{"header":"Telemetry and Remote Checks","text":"Telemetry and Remote Checks LyteNyte Grid does not collect telemetry and performs no remote validation checks. The library works entirely offline and supports sandboxed environments. No firewall configuration is required.","link":"docs/prodready-security#telemetry-and-remote-checks","depth":2},{"header":"Desktop Browsers","text":"Desktop Browsers 1771 Technologies officially supports LyteNyte Grid on the following desktop browsers: import SupportedBrowsers from \"./components/supported-browsers-desktop.astro\";","link":"docs/prodready-supported-browsers#desktop-browsers","depth":2},{"header":"Mobile Browsers","text":"Mobile Browsers 1771 Technologies officially supports LyteNyte Grid on the following mobile browsers: import MobileBrowsers from \"./components/supported-browsers-mobile.astro\";","link":"docs/prodready-supported-browsers#mobile-browsers","depth":2},{"header":"Usage of Baseline Features","text":"Usage of Baseline Features LyteNyte Grid may use any feature classified as baseline. Baseline features are available across all modern browsers. If you are unfamiliar with how baseline status is determined, see this Baseline article on web.dev for an overview. Since LyteNyte Grid limits itself to baseline features, it does not rely on any browser-specific APIs. However, some internal logic reads the user agent to identify the current browser and adjust behavior to better handle browser inconsistencies. These adjustments happen entirely within the grid and require no action from developers. Users with nonstandard environments that override the user agent should be aware that this may impact these internal checks.","link":"docs/prodready-supported-browsers#usage-of-baseline-features","depth":2},{"header":"Main Grid Types","text":"Main Grid Types There are five primary types to know about when working with LyteNyte Grid: Grid.GridSpec: A generic type constraint used by other LyteNyte Grid interfaces.Grid.Props: The interface that describes the props that the component accepts.Grid.Column: The base interface for a column in LyteNyte Grid.Grid.API: The base interface that describes the LyteNyte Grid API.RowSource: The base interface for a row data source that you can provide to the grid. All row data sources extend this base type. You do not need to memorize these types. Understanding how these types fit together will help you get the most out of TypeScript and LyteNyte Grid.","link":"docs/prodready-typescript#main-grid-types","depth":2},{"header":"Grid Specification","text":"Grid Specification Grid.GridSpec is one of the five primary grid types that developers typically define. It's a type constraint used by other grid types, and LyteNyte Grid never reads it at runtime. Grid specification has four optional type parameters and four optional properties. Each property extends the grid's type in a specific way. The base definition is as follows:","link":"docs/prodready-typescript#grid-specification","depth":2},{"header":"Specification Parameters and Properties","text":"Specification Parameters and Properties The four optional properties for Grid.GridSpec include: Data Property: LyteNyte Grid interfaces use this property to infer the structure of the row data items provided to the grid.Column Property: An extension of the base column definition provided by LyteNyte Grid. Use this property to add additional column properties that are specific to your application.Source Property: The row data source used by the grid. LyteNyte Grid uses this property to provide type completions for API methods that are specific to each row source.API Property: An extension of the base grid API that adds custom properties and methods specific to your application. The four optional type parameters for Grid.GridSpec include: Data Type: Describes the shape of the row data that the grid receives. For example, if the row data is a number[], define the grid spec like this:interface GridSpec { readonly data: number[]; }ColExt Type: Describes additional properties you want to add to columns. LyteNyte Grid combines these additional properties with the base Grid.Column type. For example, the code below shows two equivalent definitions:interface GridSpec { readonly column: { readonly sort?: \"asc\" | \"desc\" | null }; } type ColumnWithSort = Grid.Column; interface ColumnWithSort extends Grid.Column { readonly sort?: \"asc\" | \"desc\" | null; }:::infoThe GridSpec approach is recommended because it keeps column extensions and row data typing in one place.:::Source Type: Describes the row data source you provide. Each row data source shares common capabilities, but each source type also adds source-specific capabilities and functions.The source property exposes those capabilities through the grid types. The code below shows an example using the RowSourceServer type from the server row data source:interface GridSpec { readonly source: RowSourceServer; }Ext Type: Describes the API extensions that you provide through the apiExtension property on the grid. Use Ext to add user-defined functions and state to the grid's API. When you type Ext on the grid spec, you extend the API type.LyteNyte Grid also extends the API type with the provided Source type, so the final API type is the combination: Grid.API & Source & Ext.The code below shows an example that adds a notifyUser method to LyteNyte Grid's API:interface GridSpec { readonly api: { notifyUser: (msg: string) => void }; }","link":"docs/prodready-typescript#specification-parameters-and-properties","depth":3},{"header":"Combining Specification Types and Properties","text":"Combining Specification Types and Properties You can combine all properties to create a full set of specified grid types. In the example below, each part of the GridSpec is defined and used with the main types used by LyteNyte Grid: After defining these types, you can derive property-specific types. For example, define renderer types as follows:","link":"docs/prodready-typescript#combining-specification-types-and-properties","depth":3},{"header":"Namespace and Component","text":"Namespace and Component The Grid export in LyteNyte Grid is both the root component and a type namespace. The namespace contains all the types used by LyteNyte Grid's publicly exposed functions and components. This means the following code is valid: This guide does not cover the details of why this works in TypeScript, but you do not need to actively think about it. TypeScript does not allow Grid itself to be used as a type, so TypeScript prevents misuse in places where a type is expected. For example, this code fails type checking:","link":"docs/prodready-typescript#namespace-and-component","depth":2},{"header":"Component and T Namespaces","text":"Component and T Namespaces The Grid namespace has two sub-namespaces for types that are less frequently used: Grid.T: Defines auxiliary types used by the various functions and renderers of LyteNyte Grid, for example Grid.T.HeaderParams.Grid.Component: Defines the props for the various headless component parts that make up the grid. The Grid.T namespace is most useful when defining custom components. For example, the following two cell renderer definitions are equivalent. The method you choose is developer preference.","link":"docs/prodready-typescript#component-and-t-namespaces","depth":3},{"header":"Enabling Row Detail","text":"Enabling Row Detail Row detail functionality in LyteNyte Grid is always enabled. Any row can display a detail section. To create a detail section, define a rowDetailRenderer on the grid state object. LyteNyte Grid uses this renderer to render the content of each row's detail area. The demo shows row detail in action. The marker column renders a detail toggle that calls api.rowDetailToggle, which adds or removes the row ID from the rowDetailExpansions set to control which rows display an expanded detail area. ::demo[Row Detail=\"../(introduction)/demos/getting-started\"]","link":"docs/row-detail#enabling-row-detail","depth":2},{"header":"Row Detail Height","text":"Row Detail Height Use rowDetailHeight property on the grid to control the height of the detail section. The rowDetailHeight property accepts one of the following values: Number: A fixed height in pixels.Auto: The string value \"auto\", which sizes the detail section based on its content. By default, the grid uses a numeric height. Use \"auto\" when the detail content should determine its own height. ::demo[Row Detail Height=\"./demos/row-detail-height\"]","link":"docs/row-detail#row-detail-height","depth":2},{"header":"Nested Grids","text":"Nested Grids A common use case for row detail is rendering nested grids or tables. In this pattern, a master grid expands to reveal a child grid within the detail area. LyteNyte Grid supports this pattern by allowing a grid to be rendered inside a row detail section. The demo below shows a nested grid rendered inside the detail area of a row: ::demo[Nested Grid Row Detail=\"./demos/row-detail-nested\"] A nested grid is not a special case. To create one, have the detail renderer return another grid instance. LyteNyte Grid places no restrictions on what the detail renderer can display.","link":"docs/row-detail#nested-grids","depth":2},{"header":"Controlled Detail Expansion State","text":"Controlled Detail Expansion State The rowDetailExpansions property on the grid determines which rows have their detail area expanded. If you do not set rowDetailExpansions on the grid, LyteNyte Grid tracks detail expansion using internal, uncontrolled state. By providing a rowDetailExpansions value, you can control which rows have their detail area expanded to align with your application's intended behavior. By listening to onRowDetailExpansionChange, you can update your controlled expansion state accordingly. A typical use case is to limit the grid to display only one expanded row detail area at a time, so that expanding a different row's detail area automatically collapses any other open detail areas. The demo below demonstrates this behavior. ::demo[Controlled Detail Expansions=\"./demos/row-detail-controlled\"]","link":"docs/row-detail#controlled-detail-expansion-state","depth":2},{"header":"Single Row Dragging","text":"Single Row Dragging LyteNyte Grid provides the api.useRowDrag hook to enable row dragging. This hook returns drag props and handlers you can apply to a drag handle component. The handle can be any component that supports standard HTML drag-and-drop attributes, such as draggable and onDragStart. The demo below demonstrates the basic row dragging setup. Users can drag rows to reorder them within the same grid. The demo uses LyteNyte Grid's onRowDragEnter, onRowDragLeave, and onRowDrop properties to attach callbacks for drag events. ::demo[Single Row Dragging=\"./demos/row-dragging\"] The api.useRowDrag hook requires a rowIndex. LyteNyte Grid creates the necessary drag data when the drag operation begins. The code for the drag handle is shown below. The onRowDragEnter, onRowDragLeave, and onRowDrop callbacks receive parameters describing the drag operation. The parameter object has two properties: source: Data related to the row being dragged.over: Data related to the row or viewport being dragged over. Using these properties, you can perform actions when events occur, such as adding and removing drag indicators, or reordering rows.","link":"docs/row-dragging#single-row-dragging","depth":2},{"header":"Multi-Row Dragging","text":"Multi-Row Dragging Combine row dragging with row selection to support dragging multiple rows. The drag event parameters include a source property that provides the grid API for the row being dragged. Use this API to retrieve the selected rows and move them together with the current row. This is shown in the demo below. ::demo[Multiple Row Dragging=\"./demos/row-dragging-multi\"] In the demo code, p.source.api retrieves the selected rows, as shown below. The example ignores drags over the viewport, since the demo uses dragging to reorder rows. The moveRelative function is a utility provided by LyteNyte Grid. It moves items from one position to another. It performs a smart swap of rows using the indices provided.","link":"docs/row-dragging#multi-row-dragging","depth":2},{"header":"Dragging Between Grids","text":"Dragging Between Grids You can drag rows between grids using the rowDropAccept property. A grid can always drag rows within itself. To allow dragging between different grids, assign each grid an ID using the gridId property. Then share those IDs with the grids that should accept drops using the rowDropAccept property. In the demo below, the first grid is the primary grid and the second grid is the secondary grid. The grid IDs have been shared with each grid so rows can be dragged between them. ::demo[Grid-to-Grid Dragging=\"./demos/row-dragging-between-grids\"] You can identify whether the drag operation comes from the same grid or another grid using the id property on the source and over parameters, as shown below. Both the source and over properties include a reference to the api of their respective grids. This is useful when you need to retrieve information or perform actions across different grids. :::note LyteNyte Grid cannot fully type the api property on the source and over parameters, because drag operations can come from any grid. Use a type assertion when you need access to API methods specific to your grid. :::","link":"docs/row-dragging#dragging-between-grids","depth":2},{"header":"External Drop Zones","text":"External Drop Zones LyteNyte Grid's row dragging leverages the native browser drag and drop API. This means you can implement external drop zones by handling onDrop on an element. The demo below demonstrates this in action. ::demo[External Drop Zones=\"./demos/row-dragging-external\"] The getRowDragData function is a helper provided by LyteNyte Grid. This function returns the current row drag data while a row drag is active. Use this function to retrieve the drag data for your own components. Do not call this function unless a row drag operation is in progress.","link":"docs/row-dragging#external-drop-zones","depth":2},{"header":"Row Drag Placeholders","text":"Row Drag Placeholders The api.useRowDrag hook accepts an optional placeholder property. Use this property to provide a custom placeholder for drag operations. The placeholder can be one of the following: String: A CSS selector targeting the HTML element to serve as the placeholder. The browser captures a snapshot of this element to generate the placeholder image.Query Object: An object containing a query string and an optional offset tuple. This behaves like a string value but allows you to shift the placeholder image using the provided tuple.Component Placeholder: A custom React component that renders the placeholder. This option disables browser drag-image snapshots. Use this option only when drag operations stay within the same page. The demo below demonstrates a React placeholder in action. Notice that the placeholder component uses a portal to render into the body element. ::demo[Drag Placeholder=\"./demos/row-dragging-placeholder\"] When you use a React placeholder, render the placeholder value returned by api.useRowDrag, as shown below.","link":"docs/row-dragging#row-drag-placeholders","depth":2},{"header":"Drag To External Applications","text":"Drag To External Applications Since LyteNyte Grid utilizes the browser's native drag and drop API, you can drag rows into external applications. To enable this, set the data property in the api.useRowDrag hook parameters, as shown below: The data property is a record of drag data. Each entry must be one of the following types, shown below. The dt kind uses the native browser DataTransfer object. Use this kind when you want to support dragging data into external applications. In the demo below, users can drag rows into an external application that accepts the text/plain MIME type. This includes most code editors. To try it out, open your text editor and drag a row into it. ::demo[Drag To External Applications=\"./demos/row-dragging-external-app\"]","link":"docs/row-dragging#drag-to-external-applications","depth":2},{"header":"Full Width Row Predicate","text":"Full Width Row Predicate Use rowFullWidthPredicate property on the grid to determine whether a row renders as a full width row. A full width row spans the entire viewport and replaces the standard row that would otherwise render in the same position. Full width rows always span the viewport and remain visible during horizontal scrolling. Use them for section headers, summary rows, or other specialized content within the grid. In the demo below, full width rows group data by the exchange on which each crypto symbol trades. This pattern is a common use case for full width rows. ::demo[Full Width Rows=\"./demos/row-full-width\"] The demo uses rowFullWidthRenderer to provide a React component for rendering full width rows. This renderer can return any React component. In the demo, the component renders the exchange name and logo centered within the row, as shown below.","link":"docs/row-full-width#full-width-row-predicate","depth":2},{"header":"Number Row Heights","text":"Number Row Heights Set rowHeight to a number to give all rows a uniform height. The numeric value represents the row height in pixels. The demo below shows fixed row heights in action. ::demo[Fixed Row Height=\"./demos/row-height-fixed\"]","link":"docs/row-height#number-row-heights","depth":2},{"header":"Variable Row Height","text":"Variable Row Height LyteNyte Grid supports variable row heights through a function callback. This function must return a number representing the row height in pixels, and the returned value may differ per row. LyteNyte Grid calls this function for every row during layout, so performance is critical. Keep the implementation simple and avoid expensive calculations inside the callback. The demo below illustrates variable row heights in practice. ::demo[Variable Row Height=\"./demos/row-height-variable\"] In this example, the row height cycles through a predefined set of values. While this demo uses a simple pattern, your own logic can be as complex as needed, as long as it remains performant.","link":"docs/row-height#variable-row-height","depth":2},{"header":"Fill Row Height","text":"Fill Row Height Use the fill row height syntax \"fill:\" to make rows expand and fill the available viewport space, where n is the minimum row height in pixels. LyteNyte Grid assigns each row its minimum height, calculates the remaining available space, and then distributes that space evenly across all rows. This approach works well when the grid displays only a small number of rows and you want them to occupy the full viewport height. ::demo[Fill Row Height=\"./demos/row-height-fill\"] :::info When using fill row heights, if the combined minimum height of all rows exceeds the viewport height, the grid cannot distribute additional space. In this case, each row renders at exactly its minimum height, behaving the same as fixed-height rows. :::","link":"docs/row-height#fill-row-height","depth":2},{"header":"Row Node Interface","text":"Row Node Interface LyteNyte Grid represents rows using the RowNode interface. This interface is a union of three types: RowGroup: A row that contains child rows and establishes a hierarchical relationship.RowAggregated: A row that contains aggregate data but is not a group node that can be expanded.RowLeaf: A row with no child rows. You can distinguish between row node types by checking the kind property. For example: LyteNyte Grid also provides convenience methods for identifying row node types. When using TypeScript, these methods will narrow the type of the RowNode union:","link":"docs/row-overview#row-node-interface","depth":2},{"header":"Row Indices","text":"Row Indices The RowNode interface intentionally omits a row index because a row node does not always correspond to a rendered row. The grid's data source creates row nodes, and some data sources, such as the server data source, can return rows that are not yet visible. These rows do not have a defined row index. Although RowNode does not store a row index, LyteNyte Grid exposes the row index when it does exist. The grid passes the rowIndex value to components and callbacks that operate on rendered rows. Cell renderers, for example, receive a rowIndex prop, as shown in the row index demo below: ::demo[Row Indexing=\"../(column)/demos/column-marker\"] The marker column displays the row index for each rendered row. The following cell renderer adds 1 to the index because LyteNyte Grid uses zero-based indexing:","link":"docs/row-overview#row-indices","depth":2},{"header":"Pinned Row Specifications","text":"Pinned Row Specifications The grid's data source defines which rows are pinned. How you specify pinned rows depends on the type of row data source in use. The demo below uses the client row data source to demonstrate row pinning. For other data sources, see their respective guides: Tree Data SourceServer Row Pinning ::demo[Pinned Rows=\"./demos/row-pinning\"]","link":"docs/row-pinning#pinned-row-specifications","depth":2},{"header":"Pinned Row Indices","text":"Pinned Row Indices LyteNyte Grid assigns row indices sequentially from top to bottom. When the grid includes pinned rows, the first row index belongs to the first top-pinned row, and the final index belongs to the last bottom-pinned row. This indexing model has an important implication. When rows are pinned to the top, the row indices of scrollable rows are offset by the number of top-pinned rows. This matters when you work with rows by index, such as when retrieving a row programmatically. To access the first scrollable row by index, you must account for the number of rows pinned at the top. The marker column demo below makes this behavior explicit: ::demo[Offset Pinned Rows=\"./demos/row-pinning-marker\"] In this example, the first scrollable row displays an index of 3 because two rows are pinned to the top. The bottom-pinned rows display indices 17 and 18, reflecting their position after both the top-pinned and scrollable rows.","link":"docs/row-pinning#pinned-row-indices","depth":2},{"header":"Uniform Row Spans","text":"Uniform Row Spans When rowSpan is a number, every cell in the column spans the same number of rows. The grid skips rendering any cells covered by the span. The demo below shows the Symbol column spanning two rows. The first row in each span represents the current value, while the second row represents the previous value. The meaning of a span depends on your use case, but fixed numeric spans are relatively uncommon and should be used with caution. ::demo[Uniform Row Spans=\"./demos/row-spanning-number\"] Define a two-row span directly within the column definition:","link":"docs/row-spanning#uniform-row-spans","depth":2},{"header":"Function Row Spans","text":"Function Row Spans Set rowSpan to a function to compute spans dynamically per row. This allows spans based on row data. The demo below groups cryptocurrencies by exchange, with the Exchange column spanning all rows that share the same exchange value. ::demo[Function Row Spans=\"./demos/row-spanning-fn\"] The span for each exchange is computed ahead of time. The column then uses a span function to return the correct value, as shown below:","link":"docs/row-spanning#function-row-spans","depth":2},{"header":"Row Scan Distance","text":"Row Scan Distance For performance, LyteNyte Grid does not precompute spans for all rows ahead of time. Instead, it scans backward from the first visible row to determine which cells should span. The grid scans a specific number of rows before the first visible row based on the rowScanDistance property. You can change rowScanDistance to tune span calculation performance. The default scan distance is 50, which is usually sufficient for most use cases. The rowScanDistance value sets the maximum row span that a cell is allowed to have. The value returned by the rowSpan property on the column must not exceed rowScanDistance. If rowSpan exceeds rowScanDistance, LyteNyte Grid cannot guarantee that it will hide the cells covered by the span. :::info LyteNyte Grid uses a scan-based approach to optimize correctness and performance. Unlike grids that precompute spans, which limit scalability, or render spans inaccurately when scrolling, LyteNyte Grid requires setting a maximum span to maintain accuracy and performance. :::","link":"docs/row-spanning#row-scan-distance","depth":2},{"header":"Enabling Row Selection","text":"Enabling Row Selection The rowSelectionMode property on LyteNyte Grid configures the row selection behavior the grid uses. It accepts one of the following values: \"none\": Disables all built-in row selection behavior and prevents user interactions from selecting rows.\"single\": Allows the user to select a single row. Selecting a new row clears any existing selection.\"multiple\": Allows multiple rows to be selected, including additive selection behavior. In addition to rowSelectionMode, the rowSelectionActivator property controls which user action selects a row. It accepts one of the following values: \"none\": Disables all row-based selection interactions. Developers must provide a custom selection mechanism, such as selection checkboxes.\"single-click\": Selects a row when the user clicks it.\"double-click\": Selects a row when the user double-clicks it. The demo below shows single-row selection configured to select a row with a single click. Other configurations, covered in later sections, support multiple selection modes or checkbox-based selection. ::demo[Enabling Row Selection=\"./demos/row-selection\"]","link":"docs/row-selection#enabling-row-selection","depth":2},{"header":"Multiple Row Selection","text":"Multiple Row Selection Set rowSelectionMode to \"multiple\" to allow users to select more than one row. Clicking a row toggles its selection state. Holding Shift while clicking another row selects the range between the two rows. The demo below demonstrates these interactions. ::demo[Multiple Row Selection=\"./demos/row-selection-multi\"]","link":"docs/row-selection#multiple-row-selection","depth":3},{"header":"Checkbox Selection","text":"Checkbox Selection Selecting rows by clicking anywhere on the row can conflict with other grid interactions or diverge from common UX patterns. A common alternative is checkbox-based row selection. LyteNyte Grid provides the api.rowHandleSelect helper to simplify checkbox selection logic, including support for shift-based range selection. ::demo[Checkbox Row Selection=\"./demos/row-selection-checkbox\"] The demo uses api.rowHandleSelect to handle checkbox interactions: :::tip Checkbox selection and click selection are not mutually exclusive in LyteNyte Grid. You can use both at the same time. Checkboxes are a great way to indicate to users that they can select rows. :::","link":"docs/row-selection#checkbox-selection","depth":2},{"header":"Selecting All Rows","text":"Selecting All Rows The selection checkbox demo allows you to select or deselect all rows in the grid using the header checkbox in the marker column. The demo uses the SelectAll component. However, you can achieve the same select-all behavior programmatically with the api.rowSelect method, as shown below: Selecting all rows only works if rowSelectionMode is set to \"multiple\".","link":"docs/row-selection#selecting-all-rows","depth":3},{"header":"Row Selection State","text":"Row Selection State The row selection state is maintained by the row data source provided to the grid. The selection state determines which rows the grid marks as selected, and which rows are returned when you request the selected row set. While each row data source maintains its own selection state in memory, all LyteNyte Grid data sources use the same row selection state representation. :::note The explanation below applies to the built-in LyteNyte Grid data sources. If you use a custom data source, the behavior described here may not apply. ::: The row selection state may be one of two types: linked: Used when the row selection mode is \"multiple\" and when the rowsIsolatedSelection property is set to false on the data source.isolated: Used when the row selection mode is \"single\" or when rowsIsolatedSelection is set to true on the data source. The code below shows how to set linked and isolated selection when using the client row data source. The different row selection state modes primarily differ in how they handle selecting group rows. For flat datasets with no grouping, both modes behave identically.","link":"docs/row-selection#row-selection-state","depth":2},{"header":"Isolated Row Selection","text":"Isolated Row Selection Isolated row selection treats every row as individually selectable. As a TypeScript interface, it is represented as follows: With isolated selection, selecting a group row does not select its children, and selecting all the child rows of a group does not select the parent. Each row maintains its own unique selection state. RowSelectionIsolated works as an exclusion set. The selected property determines whether rows are selected by default. If selected is true, all rows are selected by default. If selected is false, no rows are selected by default. The exceptions set contains row IDs that invert the default selection: If selected is true, any row with an ID in exceptions is not selected.If selected is false, any row with an ID in exceptions is selected. The demo below demonstrates isolated row selection. Notice that selecting a group row does not select its children. This is the intended behavior of isolated row selection. ::demo[Isolated Row Selection=\"./demos/row-selection-groups\"]","link":"docs/row-selection#isolated-row-selection","depth":3},{"header":"Linked Row Selection","text":"Linked Row Selection Linked row selection represents selection state as an inclusion/exclusion tree of nodes. The type interfaces are shown below. The root of the selection tree is the RowSelectionLinked type. The selection state of a row is determined by the closest node in the tree to that row, including the row itself. Consider the tree below. Node A is not selected, but its child, Node Y, is selected, so the selection set includes Node Y. Node X is not selected because it does not override selection, and its parent is not selected. On the other hand, since Node B is selected, Node W and Node U are also selected. This gives you a selection set that includes Node W and Node U. The linked row representation may appear complex at first, but it has some very desirable properties: It compactly represents group selection states, especially when many groups are selected.It works even when only a subset of rows is loaded, crucial for server data loading where only some group rows may be available.It can be serialized into a short string, which is important for maintaining selection state in your application's URL. LyteNyte Grid compacts and collapses redundant nodes in the tree as the user selects rows. This keeps the tree as flat as possible while still representing the correct selection state. As a developer, you rarely need to interact with the selection state directly. In most cases, it is sufficient to call the rowsSelected method on the data source or the grid API. This method returns the currently selected row nodes. The demo below demonstrates linked selection. Selecting all children of a group row also selects the group row, and selecting the group row selects all its children. ::demo[Linked Row Selection=\"./demos/row-selection-groups-linked\"]","link":"docs/row-selection#linked-row-selection","depth":3},{"header":"Controlling Row Selection State","text":"Controlling Row Selection State Each LyteNyte Grid row data source maintains an internal representation of row selection state. This internal state is uncontrolled, similar to the value of an element without an onChange handler. You can control this state by setting the rowSelection property on the row data source. To listen for updates, provide an onRowSelectionChange callback. For example: :::warn LyteNyte Grid's row data sources enforce that the row selection state matches the rowsIsolatedSelection property on the data source. When providing your own row selection state, ensure that the selection type you pass matches the isolation setting. :::","link":"docs/row-selection#controlling-row-selection-state","depth":3},{"header":"Getting Selected Nodes","text":"Getting Selected Nodes All row data sources implement the rowsSelected method. Since this method is implemented on the row data source, the grid API also makes it accessible. You can call it from whichever reference is most convenient. The rowsSelected method returns the selected row nodes for the current row selection state. The nodes returned depend on the data source. For client-side data sources, such as the tree data source, all selected rows are available. For server data sources, only rows that have been loaded from the server are returned. In the demo below, selecting rows and pressing “Alert Rows” fetches those rows and triggers an alert. Use this callback to retrieve selected row data in your application. ::demo[Get Selected Rows=\"./demos/row-selection-alert\"] The Alert Rows button uses the ds.rowsSelected method to retrieve the selected rows and alert the user with the count, as shown in the code below:","link":"docs/row-selection#getting-selected-nodes","depth":2},{"header":"Preventing Row Selection","text":"Preventing Row Selection The grid uses the row data source to handle row selection state, but selection is triggered through user interactions, such as clicking a row. LyteNyte Grid fires the onRowSelect callback whenever users select rows in the grid. You can use the event parameters to prevent selection or perform a different action that aligns with your application. For example, use onRowSelect to limit row selection to 3 (or another number), as shown below. ::demo[Prevent Row Selection=\"./demos/row-selection-preventing\"] From the demo code, notice that you can call preventDefault to stop the row selection from happening:","link":"docs/row-selection#preventing-row-selection","depth":2},{"header":"Row Selection ID Universe","text":"Row Selection ID Universe Each row data source maintains an ID universe, which is a complete set of all possible row IDs currently available in that data source. For client-side data, this is the set of all possible rows for the current source configuration.For server-side data, this is the set of all loaded rows. LyteNyte Grid uses the ID universe to ensure selected row IDs are valid, meaning the ID of a selected row must exist in the grid. If you want the grid to include row IDs that are not yet created but might be added later to the data source, you can specify additional IDs using the rowSelectionIdUniverseAdditions property on the data source, as shown below: The root flag indicates that the row exists at the top level of the grid. This flag must be set and only impacts row selection when there are row groups present. Only use rowSelectionIdUniverseAdditions when the rows you want to preselect are not immediately available in the data source. To exclude rows from the ID universe, use the rowSelectionIdUniverseSubtractions property, as shown below. Rows excluded from the ID universe are not selectable. This behavior is useful when a row has a special use case and should not participate in normal selection behavior.","link":"docs/row-selection#row-selection-id-universe","depth":2},{"header":"Row Selection Reset Key","text":"Row Selection Reset Key A row's selection state is reset when the view changes in a way that makes existing rows invalid. Common actions that invalidate rows include: Row groupingFilteringSorting (only for server-side data) The rowSelectKey property on the row data source can override the default reset behavior with your own key. It functions similarly to the dependency array in useMemo or useCallback hooks. Provide an array of values, and LyteNyte Grid shallow-compares the values whenever the key changes. In the example below, clicking the Update Key button forces a row selection reset. The demo does this by setting rowSelectKey to an array containing a count, then incrementing the count whenever the button is clicked. ::demo[Resetting Row Selection=\"./demos/row-selection-reset\"]","link":"docs/row-selection#row-selection-reset-key","depth":2},{"header":"Server Data Editing","text":"Server Data Editing The useServerDataSource hook accepts a callback for the onRowDataChange property. This handler runs when the user edits a cell and sends the update request to the server. The handler runs asynchronously. Once the update completes, you must refresh the grid data, typically by calling the server data source's refresh method. In the demo below, double-click any cell to edit it. After you commit the change, the cell briefly retains its old value before the grid refreshes. To eliminate this delay, the next section explains how to optimistically update the client-side value so changes appear instantly. ::demo[Asynchronous Data Editing=\"./demos/editing-server-data\"] The example implements a basic update handler. In most cases, your update logic should look similar to the following:","link":"docs/server-data-loading-cell-editing#server-data-editing","depth":2},{"header":"Enabling Optimistic Updates","text":"Enabling Optimistic Updates To apply optimistic updates, enable rowUpdateOptimistically on the server data source. This applies edits on the client before the server responds. Since this setting assumes the server update will succeed, the change remains client-side until you refresh from the server. You must still use the onRowDataChange callback to send the update to the server and handle errors. In the demo below, double-click any cell to edit it. The cell value updates when you commit the change. ::demo[Asynchronous Optimistic Editing=\"./demos/editing-server-data-optimistic\"] When optimistic updates are enabled, you can skip calling refresh when the optimistic value matches the expected server result. The server update logic follows this pattern:","link":"docs/server-data-loading-cell-editing#enabling-optimistic-updates","depth":3},{"header":"Handling Initial Request Failure","text":"Handling Initial Request Failure When the server data source initializes, it sends an initial data request. If this fails, LyteNyte Grid can't retry specific slices because none have loaded. To recover, reset the grid as demonstrated below: ::demo[Initial Load Error=\"./demos/error-loading-initial\"] Clicking the Retry / Resolve button calls the reset method on the server data source, which resends the initial data request to the server.","link":"docs/server-data-loading-handling-load-failures#handling-initial-request-failure","depth":2},{"header":"Handle Slice Failures","text":"Handle Slice Failures Even after the initial request succeeds, subsequent requests can still fail. The LyteNyte Grid server data source provides the retry method to reattempt failed data requests. This method clears the error state and resends only the failed requests that are currently in view; failed requests outside the view are skipped but still have their error state cleared. In the following demo, scroll down to trigger failed requests. Click the Retry Failed button to clear the error state and successfully re-request the affected rows. ::demo[Failed Row Slice=\"./demos/slice-data-failures\"]","link":"docs/server-data-loading-handling-load-failures#handle-slice-failures","depth":2},{"header":"Handling Group Failures","text":"Handling Group Failures The retry method also recovers failed group expansions. Calling it re-requests all errored fetches associated with that group. In the demo below, expand a group to trigger an intentional failure, then click the exclamation icon to successfully retry the request. ::demo[Group Expansion Failure=\"./demos/error-group\"] The retry method is invoked in the GroupCellRenderer button component:","link":"docs/server-data-loading-handling-load-failures#handling-group-failures","depth":3},{"header":"Request Interface","text":"Request Interface The server data interface centers on the request interface. See the API reference for the full specification. The TypeScript definition of the DataRequest interface is shown below: Here is an example request: The request id is \"__root__:0-100”. LyteNyte Grid generates a unique id for each request but doesn't guarantee the id format, so don't use it in application logic. When two requests share the same id, they target the same rows, and their responses populate the same row range. This behavior is useful for deduplicating requests. The path, start, and end properties determine the specific row slice a request targets. The server data source divides the view into row slices. Each slice is relative to the view root or a group row node. An empty path targets the root. :::info The server data source represents rows as a tree. LyteNyte Grid flattens the tree and renders rows according to the current scroll position. In this documentation, the term \"view\" refers to the rows currently displayed. ::: The rowStartIndex and rowEndIndex project the placement of returned rows in the grid view, rather than acting as strict constraints. You can safely ignore these properties unless your server keeps the entire expanded and grouped table in memory. When you use row groups, the requested slice can target a group expansion to fetch that group's child nodes. For a grid grouped by \"Group A\" and \"Group B\", the data request is as follows: Consider the first request object, which has path: [\"Alpha\"]. The server should treat this as: “Fetch rows where Group A = \"Alpha\".” LyteNyte Grid then groups the returned rows by Group B. The second request object, with path: [\"Alpha\", \"Beta\"], targets leaf rows. In this case, Group A = \"Alpha\" and Group B = \"Beta\". This example shows how path defines the data slice the server should return. LyteNyte Grid combines these slices to build a coherent view.","link":"docs/server-data-loading-interface#request-interface","depth":2},{"header":"Response Interface","text":"Response Interface The server receives data requests and returns responses. The response types are defined below. A single request can produce multiple responses, and LyteNyte Grid's server data source supports this behavior. There are two distinct response types: Scrollable Rows: Detailed in Scrollable Rows API reference.Pinned Rows (Top or Bottom): Detailed in the Pinned Rows API Reference. LyteNyte Grid expects an array of responses. This allows the server to batch multiple response types into a single network payload.","link":"docs/server-data-loading-interface#response-interface","depth":2},{"header":"Data Response for Scrollable Rows","text":"Data Response for Scrollable Rows The DataResponse interface defines the data response for scrollable rows. The TypeScript definition is as follows: The response interface mirrors several request fields and adds additional fields. An example response looks like: The scrollable row DataResponse object contains the following properties: kind: Must be \"center\" to distinguish scrollable rows from pinned rows.data: The row data returned by the server, containing leaf or branch data items.size: Defines the relative row count for the response path. An empty path indicates the root row count. Row counts are relative to their path because grouping creates branches in the row tree. LyteNyte Grid flattens the tree to produce the rendered rows, and the final count reflects expanded and collapsed groups.asOfTime: A Unix timestamp. It resolves collisions when multiple responses map to the same row. The response with the later asOfTime takes precedence. Conflicts can occur because requests are asynchronous and responses may arrive out of order.path, start, end: Mirror the DataRequest properties. Avoid modifying these values in the response. Leaf Row Data Leaf row data uses the DataResponseLeafItem type. Its TypeScript definition is shown below: Like DataResponse, DataResponseLeafItem includes a kind property. The kind value must be \"leaf\". LyteNyte Grid uses this value to determine whether the response data creates a leaf row node. Each DataResponseLeafItem creates one row node, and the item's id maps directly to the row node's id, associating the payload's data with that specific row node. Branch Row Data Branch row data is represented by DataResponseBranchItem. LyteNyte Grid uses these items to create row group nodes. Row group nodes represent branches in the view that may be expanded or collapsed. The TypeScript interface for DataResponseBranchItem is shown below. The DataResponseBranchItem includes the following properties for group nodes: kind: Must be \"branch\" to create a group node.id: Maps to the group node's id.data: Set on the group node; it typically contains the group's aggregated data.key: Attached to the group node and used to position it within the server data source's row tree.childCount: Required. Indicates the number of child nodes in the group. The close mapping between DataResponseBranchItem and the row group node is intentional. LyteNyte Grid maps the server response directly to node creation to improve performance.","link":"docs/server-data-loading-interface#data-response-for-scrollable-rows","depth":3},{"header":"Data Response For Pinned Rows","text":"Data Response For Pinned Rows Pinned rows are always visible in the view, so LyteNyte Grid cannot detect changes to them. To address this, the server data source allows developers to push pinned row responses alongside scrollable row responses. The DataResponsePinned interface defines the response type that LyteNyte Grid expects: The pinned response type is simpler than DataResponse because pinned rows are always leaf rows. The kind property must be either \"top\" or \"bottom\", and tells LyteNyte Grid where to create the pinned row nodes. Like DataResponse, the data property contains the row data, and the asOfTime property resolves conflicts when writing rows to memory.","link":"docs/server-data-loading-interface#data-response-for-pinned-rows","depth":3},{"header":"Query Fetcher Function","text":"Query Fetcher Function The LyteNyte Grid server data source requires a queryFn as the primary data-loading handler. The server data source calls queryFn whenever the view changes and passes the requests for the new view. The function is responsible for fetching responses from the server, either with an HTTP request or a WebSocket message, and returning the data. A brief code example is shown below: The queryFn function receives a params object, which includes the data requests for the current grid view. This object is described by the QueryFnParams type whose TypeScript definition is shown below: The queryFn returns a Promise of type (DataResponse | DataResponsePinned)[]. This array-based design lets LyteNyte Grid request data in slices, giving the queryFn implementation flexibility to optimize how it fetches data and returns responses. The requests property contains the data requests for the current view. It is always an array, not a single item. Each request should have at least one matching response, but the server may return additional responses to preload data. The reqTime property is a Unix timestamp of when the request was made. The server may use it to set the asOfTime, or ignore it if responses define their own freshness rules. The model property describes the grid's view configuration. This contains the current rowGroupExpansions state. You can use this value to determine if a group is expanded or collapsed when there are row groups applied to the grid.","link":"docs/server-data-loading-interface#query-fetcher-function","depth":2},{"header":"Optimistically Fetching Data Slices","text":"Optimistically Fetching Data Slices Optimistic data loading predicts and preloads the data a user is likely to view next. Similar to prefetching linked pages to accelerate navigation, you can apply these techniques to load data slices. :::note Optimistic loading is not a fixed sequence of functions. The best approach depends on your application and user interaction patterns. This guide's demos show common use cases for optimistic loading with LyteNyte Grid. ::: The demo shows how to optimistically load slices in a flat data view. It tracks the current view and preloads the next slice when the view changes, helping the grid “stay ahead of the scroll.” Users may still see a loading indicator if they scroll faster than the next slice can load. ::demo[Optimistic Data Loading=\"./demos/optimistic-loading\"] In the demo, optimistic loading runs in a single useEffect hook, but it uses several parts of the server data source interface. The section below breaks down the full effect. For a complete list of methods, see the RowDataSourceServer reference. Multiple parts of the data source API interact to support optimistic loading. LyteNyte Grid's API is intentionally low-level so you can tailor the behavior to your use case.","link":"docs/server-data-loading-optimistic-loading#optimistically-fetching-data-slices","depth":2},{"header":"Optimistically Fetching Group Rows","text":"Optimistically Fetching Group Rows You can optimistically fetch rows when row groups are present. By detecting when a user hovers over a group row, you can prefetch the group's child rows before they expand it. See the demo below: ::demo[Optimistically Loading Group Rows=\"./demos/optimistic-loading-groups\"] Preloading on hover is one approach. Other triggers include initial load or cell focus. Each approach anticipates row expansion and loads child data before the user requests it. Preloading rows on hover is simpler than optimistic fetching on scroll: The code above uses another server data source method, requestForGroup. This method returns a DataRequest object that can be used to request the child rows of a group, allowing you to preload them in advance.","link":"docs/server-data-loading-optimistic-loading#optimistically-fetching-group-rows","depth":2},{"header":"What Problem Does Server Data Loading Solve?","text":"What Problem Does Server Data Loading Solve? When the amount of row data exceeds what is feasible to transfer to a browser at once, a partial data loading solution becomes necessary. Developers typically choose one of these approaches: Pagination: Load one page of data at a time.Infinite scrolling: Lazily load more data as the user scrolls down.Viewport-based loading: Load only the rows currently in view. LyteNyte Grid supports all three approaches. The guides in this section focus on viewport-based loading, which is flexible but can be complex to configure. Since LyteNyte Grid handles client-side loading, you only need to implement the server-side logic.","link":"docs/server-data-loading-overview#what-problem-does-server-data-loading-solve","depth":2},{"header":"Why Use Server Data Loading?","text":"Why Use Server Data Loading? Server data loading delivers a client-side experience with the expected trade-off of network latency. It provides: Direct Row Interaction: When a user scrolls to a specific index, like row 9,500, the grid requests the surrounding rows from the server.Advanced Operations: Natively supports pivoting and grouping, which are complex or limited when using pagination and infinite scrolling.","link":"docs/server-data-loading-overview#why-use-server-data-loading","depth":2},{"header":"Enabling Server Data Source","text":"Enabling Server Data Source To enable the server data source, developers must provide LyteNyte Grid with: Total Row Count: Sets the size of the scrollable area. With row grouping, provide group-specific row counts that the grid combines to represent the full dataset.queryFn: A function LyteNyte Grid calls to fetch rows from your server.","link":"docs/server-data-loading-overview#enabling-server-data-source","depth":2},{"header":"Server Data Flow","text":"Server Data Flow The server data source follows a well defined lifecycle: Other operations extend this basic flow. LyteNyte Grid's server data source supports various advanced configurations. A simple example of a server data loading grid is shown below. ::demo[Basic Server Data=\"./demos/basic-server-data\"]","link":"docs/server-data-loading-overview#server-data-flow","depth":2},{"header":"Pulling Data","text":"Pulling Data Use the pushRequests method on the server data source to manually trigger server data fetches. Calling this method bypasses existing request tracking and processes these requests as new. The grid resolves row data conflicts and cancels pending requests if the grid resets before the server responds. Click the Request Data button in the demo below to manually refresh the grid, simulating a user-triggered polling update. ::demo[Server Data Pulling=\"./demos/data-pulling\"] The Request Data button's onClick callback handles the server data fetch: The demo uses the requestsForView atom to fetch data for the current view, though any request set can be used. If you're unfamiliar with the request interface, see the Data Interface guide for more details.","link":"docs/server-data-loading-push-and-pull#pulling-data","depth":2},{"header":"Pushing Data","text":"Pushing Data Use pushResponses on the server data source to push data into LyteNyte Grid. It accepts complete data responses and applies them to the data source. You can also generate client-side updates and push them as simulated server responses, as demonstrated below. ::demo[Server Data Pushing=\"./demos/data-pushing\"] This example is intentionally simple to highlight the core functionality. Like the data-pulling example, the main logic runs in the Push Data button's onClick handler. However, instead of calling pushRequests, it calls pushResponses and provides complete responses. Pushing responses requires understanding the server data source response model and how responses form the data tree. For details, see the Data Interface guide.","link":"docs/server-data-loading-push-and-pull#pushing-data","depth":2},{"header":"Server Data Source Hook","text":"Server Data Source Hook To use the server data source, import the useServerDataSource hook from @1771technologies/lytenyte-pro. Since this function is a React hook, it must follow the Rules of Hooks. Provide the useServerDataSource hook with a queryFn callback. The callback retrieves data from the server. LyteNyte Grid passes it parameters describing the current data request and expects a promise that resolves to the data response. The full interfaces and request/response cycle are described in the Data Interface guide. A complete example of basic server loading is shown below: ::demo[Server Row Data=\"./demos/basic-server-data\"] When the grid loads data from the server, cell renderers and grid configuration remain client-side. In this example, the server returns only row data.","link":"docs/server-data-loading-row-data#server-data-source-hook","depth":2},{"header":"Server Column Setting","text":"Server Column Setting If the server defines the column configurations, include the column definitions in the response. The example below extends the queryFn to handle this scenario. ::demo[Server Defined Columns=\"./demos/server-data-with-columns\"] The client does not define columns up front. LyteNyte Grid requests the initial data, and the server returns both row data and column definitions. The queryFn then sets the grid columns:","link":"docs/server-data-loading-row-data#server-column-setting","depth":2},{"header":"Initial Data Loading State","text":"Initial Data Loading State Use the server data source's isLoading state to show loading indicators or skeleton placeholders during the initial data fetch. The example below shows the loading state. The demo response never resolves, so data never appears: ::demo[Initial Loading State=\"./demos/always-loading\"]","link":"docs/server-data-loading-row-data#initial-data-loading-state","depth":2},{"header":"Initial Data Loading Error","text":"Initial Data Loading Error Network requests can fail. Failures may occur during: The initial data request.A subsequent data request (for example, when scrolling through rows). This section introduces error handling using a simple failure case. See the Handling Load Failures guide for best practices and more details. ::demo[Initial Load Error=\"./demos/error-loading-initial\"] When the initial request fails, the server data source sets the reactive loadError property: :::note LyteNyte Grid can only detect a failed request if the promise returned by the queryFn rejects. If your implementation catches and handles the rejection internally, the grid will not detect the error. This is acceptable when the error is handled intentionally, but it's a common source of confusion. For example, this queryFn never reports a failure: :::","link":"docs/server-data-loading-row-data#initial-data-loading-error","depth":2},{"header":"Filtering Rows","text":"Filtering Rows To filter rows on the server, define a filter model and include it in each data request. The useServerDataSource hook accepts a queryKey array. When any value in the array changes, the server data source resets and fetches new rows. Include the filter model in the queryKey dependency array. The data source passes the queryKey value to the queryFn. The queryFn then sends the filter model to the server, which applies the filter before returning the data. The following demo passes a custom filter model to the queryKey property. Click the funnel icon in the column header to apply a filter. ::demo[Server Row Filtering=\"./demos/filtered-server-data\"] To filter rows, the demo creates a filter model and sends it to the server. The filter model interface appears below. The server uses the GridFilter type to evaluate and filter rows. To send the filter model to the server, add it to the queryKey as shown below. The server then evaluates the filter model and returns only the matching rows. This is just one approach to server-side filtering. We recommend aligning your client-side filter model with your existing server format to maintain consistency.","link":"docs/server-data-loading-row-filtering#filtering-rows","depth":2},{"header":"Server Grouped Rows","text":"Server Grouped Rows To group rows in the server data source, define a custom row group model. The simplest approach uses an array of string values, where each value is the ID of a column to group by. Send this model to the server to request grouped rows. The example below shows this behavior. For details on the data request interface used by the server data source, see the Data Interface guide. ::demo[Basic Server Row Grouping=\"./demos/row-grouping-basic\"] This basic grouping example excludes aggregations, so only leaf-level rows contain data values. The demo's server.ts file shows the parent/child logic required to build and return grouped rows. Compared to flat rows, grouped rows add parent/child structure to the data model. To support this hierarchy, the grid includes a path property in each request to identify the specific group being fetched. Return the same path in the response so the server data source can store the returned rows in its internal data tree.","link":"docs/server-data-loading-row-grouping-and-aggregation#server-grouped-rows","depth":2},{"header":"Server Row Aggregations","text":"Server Row Aggregations To display values on group rows, return group rows with aggregated data from the server. In the demo, click the aggregation name in a column header to change the aggregation. Each change updates the queryKey, which refreshes the server data source and updates the view with new values. ::demo[Aggregated Server Row Grouping=\"./demos/row-grouping-aggregations\"] Not every column requires aggregation, especially text columns. The server must return correct aggregate values, as LyteNyte Grid does not validate them and simply trusts the server's response. Many aggregation types are supported. In most cases, these correspond to the aggregation functions available in your database. Common examples include sum and average, while advanced databases like ClickHouse provide additional functions listed here.","link":"docs/server-data-loading-row-grouping-and-aggregation#server-row-aggregations","depth":2},{"header":"Displaying Child Counts","text":"Displaying Child Counts When grouping rows, it is sometimes useful to display the direct child count on each parent row. This helps visualize the size of each group. To support this, the server can include a child count value for each group in the response data. The demo below illustrates this by showing child counts in the group cells. ::demo[Row Grouping with Child Counts=\"./demos/row-grouping-child-counts\"] :::note Include child counts directly in the row data to make them accessible to cell renderers. The child count property on the group row response belongs to the server data loading interface, not the row interface. :::","link":"docs/server-data-loading-row-grouping-and-aggregation#displaying-child-counts","depth":2},{"header":"Server Pinned Rows","text":"Server Pinned Rows The server data source does not track viewport changes for pinned rows because they remain fixed. To supply pinned rows, return a DataResponsePinned object for every viewport request, including the initial load. For details, see the Data Interface guide. This example pins rows to the top and bottom of the grid. The server always returns both pinned and scrollable rows. Note that pinned rows are always leaf rows. ::demo[Server Row Pinning=\"./demos/row-pinned\"]","link":"docs/server-data-loading-row-pinning#server-pinned-rows","depth":2},{"header":"Pushing Pinned Row Updates","text":"Pushing Pinned Row Updates The server can update pinned rows on any server data source request. To update pinned rows without a viewport change, call the server data source's pushResponses method: The pushResponses method lets the client push response objects into the server data source as if the server returned them. In the demo below, Pin One Top pins a single row to the top, and Remove Pinned removes the pinned row. Both actions run on the client without a server response. ::demo[Pushing Pinned Rows=\"./demos/row-pinned-pushed\"] pushResponses is one way to push data into the grid. For more details, see the Data Pushing or Pulling guide.","link":"docs/server-data-loading-row-pinning#pushing-pinned-row-updates","depth":2},{"header":"Sort Rows On The Server","text":"Sort Rows On The Server To sort rows on the server, define a sort model and include it in each data request. The useServerDataSource hook accepts a queryKey property, which is an array of dependencies for data fetching. When a value in this array changes, the data source resets and fetches a new set of rows. Include your sort model in the queryKey dependency array. The data source passes the queryKey values to the queryFn. Your queryFn then sends the sort model to the server, which applies the sort before returning the data. The following demo passes a custom sort model to the queryKey property. Clicking a column header updates that column's sort state. ::demo[Server Row Sorting=\"./demos/server-row-sorting\"] The demo applies sort state to individual columns. You can use the grid's API to update each column's sort property. The component then derives the sort model from the column definitions and passes it to the queryKey dependency array. :::tip LyteNyte Grid is unopinionated about sort model structures. Align your client-side sort model with your database schema to minimize translation logic when building server queries. :::","link":"docs/server-data-loading-row-sorting#sort-rows-on-the-server","depth":2},{"header":"Processing Updates","text":"Processing Updates By default, the server data source doesn't poll for row updates and requests new data only when the view changes. To request data for the current view on demand, call the data source refresh method. You can build a real-time grid in two steps: Set up the data grid view using the standard non-updating configuration with the useServerDataSource hook.Call the refresh method whenever the grid should update its rows. The demo below uses setInterval to poll the server every second, then calls refresh on each interval to fetch updated rows. It uses a grouped view to show that update ticks work for both flat and grouped data. ::demo[Server Data Ticking=\"./demos/row-data-updates\"]","link":"docs/server-data-loading-row-updating#processing-updates","depth":2},{"header":"Manually Refreshing the Grid","text":"Manually Refreshing the Grid The refresh method is a convenience wrapper around two underlying server data source methods: requestsForView: Returns data request objects for the current view.pushRequests: Sends data request objects to the server to force an update for the specified slices. The following code blocks are equivalent:","link":"docs/server-data-loading-row-updating#manually-refreshing-the-grid","depth":3},{"header":"Data Updating Considerations","text":"Data Updating Considerations Live ticking data grids introduce several subtle and complex synchronization challenges. The LyteNyte Grid server data source handles these complexities automatically.","link":"docs/server-data-loading-row-updating#data-updating-considerations","depth":2},{"header":"High-Frequency Updates","text":"High-Frequency Updates Some views can display hundreds or thousands of visible cells that can update simultaneously and at high frequency. LyteNyte Grid renders these visual updates efficiently, but server-driven updates can bottleneck in your application's data-processing layer: Serialization: Browser serialization and deserialization of server payloads can add overhead.Network limits: You'll often hit network update limits before LyteNyte Grid hits cell-rendering limits. To optimize performance, batch updates when the update rate is high. For example, call refresh every 5 seconds to apply updates in 5-second intervals. The exact timing depends on your specific use case. If you notice performance dips, they are likely in your application's data-processing layer.","link":"docs/server-data-loading-row-updating#high-frequency-updates","depth":3},{"header":"Consistent Group Updates","text":"Consistent Group Updates When row groups are present, data requests occur per group row and its children. For example, if a single expanded row group is in view, the view consists of: The root data slice containing the group rowsThe child rows of the expanded group These represent two separate data requests. LyteNyte Grid's queryFn provides both slices simultaneously, but if your implementation splits them into multiple server requests, the returned data may become out of sync. This can occur if the server data ticks between requests. To prevent this, handle refresh requests together to ensure data consistency.","link":"docs/server-data-loading-row-updating#consistent-group-updates","depth":3},{"header":"Server Tree Data Display","text":"Server Tree Data Display LyteNyte Grid's server data source renders the rows returned by the server, not the models defined in the grid state. For example, if the row group model defines four grouping levels, the grid does not force every row to expand four times to reach a leaf node. The server data source can display tree data of any depth, provided the server's data representation stays consistent across requests and responses. The demo demonstrates this by creating a tree with varying branch depths. It does not use row groups, but the same pattern applies. ::demo[Unbalanced Rows=\"./demos/unbalanced-rows\"] This is one way to handle unbalanced hierarchies. Another common approach collapses row groups with a single child. The server interprets state, and LyteNyte Grid renders the rows it returns.","link":"docs/server-data-loading-unbalanced-rows#server-tree-data-display","depth":2},{"header":"CSS Modules Setup","text":"CSS Modules Setup Depending on your framework, CSS modules may work without additional configuration. For example, when using Vite, any CSS file ending with .module.css is treated as a CSS module. Next.js follows the same convention. If you are not using Next.js or Vite, consult your framework's documentation. If needed, refer to the official CSS Modules documentation.","link":"docs/grid-theming-css-modules#css-modules-setup","depth":2},{"header":"Styling With Scoped Styles","text":"Styling With Scoped Styles To style LyteNyte Grid using CSS modules, create a module file and define classes that target grid elements through their data attributes, such as data-ln-header-cell and data-ln-cell. The example below illustrates this: ::demo[Styling With CSS Modules=\"./demos/styling-with-modules\"] The styles.cell class only applies to . This scoping behavior is the main benefit of CSS modules, as styles in one module don't conflict with class names defined elsewhere. Now import the module and attach the styles to the appropriate LyteNyte Grid elements: The styles.cell class applies only to . This scoping behavior is the primary advantage of CSS modules—styles in one module do not conflict with class names defined elsewhere.","link":"docs/grid-theming-css-modules#styling-with-scoped-styles","depth":2},{"header":"Styling Elements Not Directly Exposed","text":"Styling Elements Not Directly Exposed The Grid Theming guide explains that some LyteNyte Grid elements are not directly exposed through its public component interface. You must style these elements using data attributes or other explicit selectors. When using CSS modules, nested selectors are the recommended way to target these elements.","link":"docs/grid-theming-css-modules#styling-elements-not-directly-exposed","depth":2},{"header":"Styling Row Detail","text":"Styling Row Detail To style the row-detail element with CSS modules, target the element that has the data-ln-row-detail attribute. The example below demonstrates this by defining a rowDetail class in a CSS module. ::demo[CSS Modules Styling Row Detail=\"./demos/row-detail-modules\"] Here is the associated CSS module. The nested selectors make the rule slightly more complex, but still easy to follow: Apply the rowDetail class to the grid row:","link":"docs/grid-theming-css-modules#styling-row-detail","depth":3},{"header":"Styling Cell Selection Ranges","text":"Styling Cell Selection Ranges You can style cell-selection rectangles using the same nested selector pattern. Create a class that targets the data-ln-cell-selection-rect attribute and apply it at an appropriate wrapper level. The example below demonstrates this approach: ::demo[Cell Selection Styling With CSS Modules=\"./demos/cell-selection-rect-modules\"] Here is the CSS module used to style the selection rectangle:","link":"docs/grid-theming-css-modules#styling-cell-selection-ranges","depth":3},{"header":"CSS Modules and Pre-built Themes","text":"CSS Modules and Pre-built Themes You can style LyteNyte Grid entirely with CSS modules, but this is not required. You may also combine a pre-built LyteNyte Grid theme with custom CSS module classes. The example below demonstrates how to enhance an existing theme: ::demo[Pre-built Themes With CSS Modules=\"./demos/grid-theming-modules\"] In the demo, the pre-built theme provides the base styling, and CSS modules add additional cell-specific styles. This works seamlessly because the pre-built themes are plain CSS and CSS modules simply scope class names.","link":"docs/grid-theming-css-modules#css-modules-and-pre-built-themes","depth":2},{"header":"Emotion Setup","text":"Emotion Setup To start using Emotion, install the @emotion/styled package: This package provides Emotion's styled-component API and lets you create pre-styled components: We apply this pattern to style LyteNyte Grid components.","link":"docs/grid-theming-emotion#emotion-setup","depth":2},{"header":"Creating Styled LyteNyte Grid Components","text":"Creating Styled LyteNyte Grid Components LyteNyte Grid is headless and exposes root primitives you can style individually. Emotion accepts arbitrary components, and the grid primitives align with Emotion's API, so creating styled components is straightforward. For example, you can create a styled Grid.Cell: Use the Cell component in place of Grid.Cell when building the grid view. The following example demonstrates this and also defines a styled header. ::demo[Styling With Emotion=\"./demos/styling-with-emotion\"]","link":"docs/grid-theming-emotion#creating-styled-lytenyte-grid-components","depth":2},{"header":"Styling Elements Not Directly Exposed","text":"Styling Elements Not Directly Exposed The Grid Theming guide explains that some elements are not directly exposed through LyteNyte Grid's public component interface. You must style these elements using data attributes or any other selector strategy. When using Emotion, you can target these elements with nested CSS selectors inside a styled wrapper.","link":"docs/grid-theming-emotion#styling-elements-not-directly-exposed","depth":2},{"header":"Styling Row Detail","text":"Styling Row Detail To style the row-detail element, wrap the Grid.Viewport or Grid.RowsContainer components in a styled wrapper and target the element with the data-ln-row-detail attribute. The example below shows this approach. ::demo[Emotion Styling Row Detail=\"./demos/row-detail-emotion\"] Here is the styled wrapper used to target the row-detail container:","link":"docs/grid-theming-emotion#styling-row-detail","depth":3},{"header":"Styling Cell Selection Ranges","text":"Styling Cell Selection Ranges Cell-selection rectangles can also be styled using a nested data-attribute selector within a styled wrapper. The example below demonstrates this: ::demo[Cell Selection Styling With Emotion=\"./demos/cell-selection-rect-emotion\"] Here is the styled wrapper that targets the cell-selection rectangle:","link":"docs/grid-theming-emotion#styling-cell-selection-ranges","depth":3},{"header":"Emotion And Pre-built Themes","text":"Emotion And Pre-built Themes LyteNyte Grid provides pre-styled themes implemented in plain CSS. These themes work with any CSS framework, including Emotion. You can include a pre-built theme in your application and layer Emotion-based styles on top. The example below demonstrates this: ::demo[Pre-built Themes With Emotion=\"./demos/grid-theming-emotion\"]","link":"docs/grid-theming-emotion#emotion-and-pre-built-themes","depth":2},{"header":"Tailwind Theming Setup","text":"Tailwind Theming Setup Start by setting up Tailwind. The exact steps depend on your React framework. See Tailwind's Installation guide for framework-specific details. This guide assumes you have Tailwind configured with the default setup, and a CSS file that contains: If you are not sure which framework to use, we recommend starting with Vite. The project setup is simple and Tailwind is supported by an official plugin.","link":"docs/grid-theming-tailwind#tailwind-theming-setup","depth":2},{"header":"Applying Tailwind Classes","text":"Applying Tailwind Classes LyteNyte Grid exposes a set of headless components that you use to build the grid view. These components accept standard HTML and React props, including className. As a result, styling the grid with Tailwind is as simple as applying utility classes. The example below demonstrates this using the standard Tailwind color palette: ::demo[Styling With Tailwind=\"./demos/tailwind-styles\"] In the example, Tailwind classes are applied in the usual way. No special configuration is required. For example, the Grid.Cell component is styled as follows:","link":"docs/grid-theming-tailwind#applying-tailwind-classes","depth":2},{"header":"Class Variance Authority","text":"Class Variance Authority Directly applying classes to elements works well for simple styling, but it does not scale when there is a lot of conditional styling being applied. Tailwind utility helpers can make conditional styles easier to manage. The approach here uses three libraries: tailwind-mergeclsxclass-variance-authority Start by creating a tw utility function that combines clsx and tailwind-merge: :::info If you already use a Tailwind-based framework such as shadcn/ui, a similar tw function may already exist, often named cn in shadcn/ui projects. ::: Next, define grid-cell variants using class-variance-authority: The cellStyles function now returns the base classes and applies variants on top for different grid-cell states. The example below shows how to combine Tailwind, tw, and cva to provide rich, composable grid styles: ::demo[Tailwind Styling With CVA=\"./demos/tailwind-cva\"] Notice how the CVA-generated style is applied to the grid cell: This pattern keeps styles consistent and makes it easy to maintain variants over a shared base.","link":"docs/grid-theming-tailwind#class-variance-authority","depth":3},{"header":"Styling Elements Not Directly Exposed","text":"Styling Elements Not Directly Exposed The Grid Theming guide explains that some elements are not directly exposed through LyteNyte Grid's public component interface. You must style these elements using data attributes or other selectors. You can still target these elements with Tailwind by using more advanced selector syntax. This section shows how to style the row-detail element and the cell selection ranges with Tailwind.","link":"docs/grid-theming-tailwind#styling-elements-not-directly-exposed","depth":2},{"header":"Styling Row Detail","text":"Styling Row Detail To style the row-detail element with Tailwind, target the element that has the data-ln-row-detail attribute. You can do this without a separate CSS file by using Tailwind's arbitrary variant selector syntax. ::demo[Tailwind Styling Row Detail=\"./demos/row-detail-tailwind\"] The selector (shown below) looks complex but it is straightforward. The & symbol refers to the current element. The [data-ln-row-detail=\"true\"] portion selects the descendant that has the matching data attribute. Finally, >div selects the div that is a direct descendant. If you are unfamiliar with this syntax, see Tailwind's guide on arbitrary variants. This example styles the row-detail element inline. You can also move these styles into a separate CSS file and target the same data attribute selectors there. Tailwind compiles down to vanilla CSS, so both approaches work.","link":"docs/grid-theming-tailwind#styling-row-detail","depth":3},{"header":"Styling Cell Selection Ranges","text":"Styling Cell Selection Ranges You can style cell-selection rectangles using arbitrary Tailwind selectors in a similar way. In this case, you place the selector on the rows container rather than on each row. The example below demonstrates this: ::demo[Cell Selection Styling With Tailwind=\"./demos/cell-selection-rect-tailwind\"] Here we use a descendant selector to target every cell selection rectangle. This is a direct inline approach, but you can also move these rules to a separate CSS file if you prefer.","link":"docs/grid-theming-tailwind#styling-cell-selection-ranges","depth":3},{"header":"Mixing Tailwind and Pre-built Themes","text":"Mixing Tailwind and Pre-built Themes Fully theming LyteNyte Grid from scratch can require a significant amount of styling. If you want the grid to match your design system exactly, you may choose to define every style yourself. However, starting from a pre-built theme and then adjusting styles with Tailwind is often more efficient. Because Tailwind does not restrict other CSS inputs, you can combine a pre-built LyteNyte Grid theme with Tailwind utilities. This is how many of the demos on the 1771 Technologies website are built. The example below shows Tailwind working alongside the provided grid themes: ::demo[Pre-built Themes With Tailwind=\"./demos/grid-theming\"] :::warn Depending on the order of CSS imports in your application, Tailwind styles may override LyteNyte Grid styles. This happens because the pre-built LyteNyte Grid styles are defined in the ln-grid CSS layer. To ensure Tailwind and LyteNyte Grid work together as expected, set the layer order in your application as follows: For more guidance on CSS layers, see this MDN guide. :::","link":"docs/grid-theming-tailwind#mixing-tailwind-and-pre-built-themes","depth":2},{"header":"LyteNyte Tailwind Variants","text":"LyteNyte Tailwind Variants To make styling LyteNyte Grid with Tailwind easier, the LyteNyte Grid packages export a Tailwind-specific CSS file with useful variants and Tailwind theme values. The full content is available on our GitHub repository. To use the LyteNyte Grid Tailwind variants, import the file in the same CSS file where you set up Tailwind: After you import it, you can use the custom variants defined in the file. For example, the ln-cell variant sets the cell background in the grid to the value of the ln-bg token.","link":"docs/grid-theming-tailwind#lytenyte-tailwind-variants","depth":2},{"header":"Next Steps","text":"Next Steps This guide covered the basics of styling LyteNyte Grid with Tailwind. For more details, see: ::next[/docs/intro-installation-shadcn] ::next[/docs/grid-theming] ::next[/docs/cell-renderers]","link":"docs/grid-theming-tailwind#next-steps","depth":2},{"header":"Pre-built Themes","text":"Pre-built Themes The fastest way to create a visually polished grid is by using one of the built-in LyteNyte Grid themes. To apply a theme, add the ln-grid class to any HTML element above the grid, then include the class name of the theme you want to use. Ensure you have imported the CSS file for the themes: LyteNyte Grid provides seven ready-to-use themes. Add the theme class, alongside the ln-grid class, to a parent element of the grid (typically html or body): ln-teal: Teal-accent theme.ln-term: Terminal-style color theme.ln-dark: Standard dark theme.ln-light: Standard light theme.ln-shadcn: A theme based on the Shadcn color tokens. Supports both light and dark mode. Use in projects that adopt shadcn.ln-cotton-candy: Playful, colorful theme for friendly data grid views. The demo below shows how each theme looks in practice. For a complete example with auxiliary components, visit our live demo page, which includes a theme toggle. ::demo[Pre-built LyteNyte Grid Themes=\"./demos/grid-theming\"] You can also create a custom theme from scratch. LyteNyte Grid is unopinionated about your styling method and works with any approach.","link":"docs/grid-theming#pre-built-themes","depth":2},{"header":"CSS Exports","text":"CSS Exports LyteNyte Grid aims to minimize the final bundle size produced by your application, including the size of your bundled CSS. For this reason, the LyteNyte Grid packages export individual CSS themes so that unused themes can be excluded from your final CSS bundle. The exports are shown below with a comment explaining their purpose.","link":"docs/grid-theming#css-exports","depth":3},{"header":"LyteNyte Design Tokens","text":"LyteNyte Design Tokens The pre-built LyteNyte themes use a set of design tokens to maintain a consistent look and feel. These tokens are CSS variables that your application can modify to augment or adjust the existing themes. See the design.css file in our GitHub repository for the definitions of the non-color design tokens.See the ln-dark.css file in our GitHub repository for the definitions of the color design tokens. All themes use the same set of color token names. The rest of this guide explains how to style the grid using vanilla CSS and inline styles. Even if you use a framework like Tailwind, reviewing these examples will help you understand the styling attributes available in LyteNyte Grid.","link":"docs/grid-theming#lytenyte-design-tokens","depth":3},{"header":"Styling Through Data Attributes","text":"Styling Through Data Attributes LyteNyte Grid makes extensive use of data attributes on grid elements. Data attributes are custom pieces of information attached to an HTML element. They always begin with the data- prefix. For example, each cell element includes the attribute data-ln-cell=\"true\". All LyteNyte Grid data attributes begin with the data-ln prefix, where ln stands for LyteNyte. Using these attributes is an effective way to target specific parts of the grid for CSS styling. In the demo below, the header cells and grid cells are styled using HSLA color codes and the light-dark CSS function, which is useful for specifying theme colors for light and dark color schemes. ::demo[Data Attribute Styling=\"./demos/data-attributes\"] The CSS uses the attribute selector, as shown below: Wrap the styles in a data-styles class to apply them on a per-grid basis. If you prefer to apply them globally, remove the wrapper class.","link":"docs/grid-theming#styling-through-data-attributes","depth":2},{"header":"Commonly Used Data Attributes","text":"Commonly Used Data Attributes It is useful to understand which data attributes apply to each element. You can inspect the rendered grid in your browser's developer tools, but the most common attributes are listed below for convenience.","link":"docs/grid-theming#commonly-used-data-attributes","depth":3},{"header":"Header Elements","text":"Header Elements Each header element includes an attribute indicating its type: Grid.Viewport: data-ln-viewportGrid.Header: data-ln-headerGrid.HeaderRow: data-ln-header-rowGrid.HeaderGroupCell: data-ln-header-groupGrid.HeaderCell: data-ln-header-cell Additionally, Grid.HeaderCell may include data-ln-header-floating to indicate that the cell belongs to the floating header. Some elements also include attributes for contextual styling. For example, Grid.HeaderCell includes data-ln-colpin to indicate the pin section (start, center, or end). It may also include data-ln-last-start-pin or data-ln-first-end-pin to indicate whether the cell is the last in the start section or the first in the end section. Grid.HeaderGroupCell includes attributes describing its state. The data-ln-collapsible attribute indicates that the group cell can collapse, while data-ln-collapsed reflects the current collapse state. Use these attributes to precisely target header elements for custom styling.","link":"docs/grid-theming#header-elements","depth":3},{"header":"Row and Cell Elements","text":"Row and Cell Elements Each row and cell element includes an identifying data attribute: Grid.RowsContainer: data-ln-rows-containerGrid.RowsTop: data-ln-rows-topGrid.RowsCenter: data-ln-rows-centerGrid.RowsBottom: data-ln-rows-bottomGrid.Row: data-ln-rowGrid.RowFullWidth: data-ln-row and data-ln-rowtype=\"full-width\"Grid.Cell: data-ln-cell Additional contextual attributes support conditional styling. For example, Grid.Row may include data-ln-last-top-pin or data-ln-first-bottom-pin to indicate whether the row is the last top-pinned or first bottom-pinned row. Using these attributes provides a consistent and reliable way to target specific parts of LyteNyte Grid for styling.","link":"docs/grid-theming#row-and-cell-elements","depth":3},{"header":"Styling Through Classes","text":"Styling Through Classes LyteNyte Grid provides flexible styling options. Since each component is exposed, you can apply any number of classes to its elements. Use this approach when you have an existing design system and want LyteNyte Grid to match it. ::demo[Styling With Classes=\"./demos/styling-with-classnames\"] The CSS is straightforward. The example below shows how the classes are applied through the className property on the components.","link":"docs/grid-theming#styling-through-classes","depth":2},{"header":"Styling Through Inline Styles","text":"Styling Through Inline Styles You can also style LyteNyte Grid elements using inline styles. This approach works well when style values are dynamic and available in JavaScript. For example, a simple theme editor can be built this way, although it is not generally recommended. ::demo[Inline Styles=\"./demos/grid-inline-styles\"] Inline styles are applied directly to each component. For example, the Grid.Cell component can use the style property as shown below. :::info Use caution when applying inline styles. The same maintainability issues and limitations of inline styles also apply in LyteNyte Grid. When used selectively, inline styles can simplify code, but avoid using them for every grid element. :::","link":"docs/grid-theming#styling-through-inline-styles","depth":2},{"header":"Styling Elements Not Directly Exposed","text":"Styling Elements Not Directly Exposed Most LyteNyte Grid components are accessible through the headless interface. However, for convenience, some advanced features are rendered and controlled internally by LyteNyte Grid. These elements include: The row detail container (master detail container).The cell selection rectangles. To style these elements, you must target them directly using CSS. In most cases, a single selector is enough to apply your styles. If you are using one of our pre-built themes, these elements are already styled.","link":"docs/grid-theming#styling-elements-not-directly-exposed","depth":2},{"header":"Styling the Row Detail (Master Detail) Container","text":"Styling the Row Detail (Master Detail) Container The example below shows a grid with a single row detail expanded. Styles for the detail container are applied by targeting the row element that includes the data-ln-row-detail attribute. ::demo[Styling Row Detail=\"./demos/row-detail-styling\"] The applied styles are simple. Padding is added to the detail container, and a border is applied to its direct div descendant. You can adapt this approach as needed to fit your use case.","link":"docs/grid-theming#styling-the-row-detail-master-detail-container","depth":3},{"header":"Styling the Cell Selection Rectangle","text":"Styling the Cell Selection Rectangle The cell selection rectangle consists of div elements created by LyteNyte Grid to show the range of selected cells. This is a PRO feature available in LyteNyte Grid PRO. You can style the selection rectangles using data attributes, primarily data-ln-cell-selection-rect. ::demo[Cell Selection Styling=\"./demos/cell-selection-rect\"] In this example, the cell selection rectangle is styled using the CSS below, which targets the selection rectangles generated by LyteNyte Grid when selection ranges are active. Notice how individual rectangles are selected, as the selection area is actually a set of split divs assembled to visually form the selection rectangle.","link":"docs/grid-theming#styling-the-cell-selection-rectangle","depth":3},{"header":"Style Overrides","text":"Style Overrides LyteNyte Grid is headless, but the default view setup is usually the recommended way to render grid cells. Sometimes you need to apply styles directly to a cell or header, and normal CSS selectors are insufficient (see the sticky column group labels demo for an example). In these cases, use the styles property on the grid to apply CSS classes or inline styles to specific grid components. This is demonstrated in the example code below: Both approaches are equivalent, and you do not pay a performance penalty for choosing one over the other. You also do not need to memoize the styles property. LyteNyte Grid diffs the styles object and notifies cells when the styles change. For more guidance on the headless setup of LyteNyte Grid, see the Headless Component Parts guide.","link":"docs/grid-theming#style-overrides","depth":2},{"header":"Layout Constraints","text":"Layout Constraints LyteNyte Grid is mostly headless and unstyled, but some functional inline styles are necessary for layout and rendering. These styles control cell sizing and positioning. Avoid overriding them to ensure the grid renders correctly. Functional styles typically affect properties such as width, height, top, left, and transform. Avoid applying margin directly to grid elements, as it may interfere with layout calculations. LyteNyte Grid applies its functional styles inline, which gives them high specificity. As a result, it is difficult to unintentionally override these styles. In most cases, conflicts will only occur if you create an extremely specific CSS selector.","link":"docs/grid-theming#layout-constraints","depth":2},{"header":"Next Steps","text":"Next Steps This guide covers the main styling approaches for LyteNyte Grid. Since most developers use a framework or CSS abstraction, we also provide dedicated guides for popular styling methods. ::next[/docs/grid-theming-tailwind] ::next[/docs/grid-theming-css-modules] ::next[/docs/grid-theming-emotion]","link":"docs/grid-theming#next-steps","depth":2},{"header":"Editing Tree Cells","text":"Editing Tree Cells To edit a tree cell, make the column for that cell editable and provide an editRenderer component. You must also set the editMode property on the grid to \"cell\" or \"row\". When a cell is edited, LyteNyte Grid calls the onRowDataChange method on the tree data source. Set the onRowDataChange property on the useTreeDataSource hook to handle these data updates. The onRowDataChange handler receives the applied cell changes, along with any data changes applied to rows pinned to the top or bottom of the grid. The full function interface is shown below: The demo below shows tree cell editing. Double-click a cell in the Size column to start editing. ::demo[Tree Data Editing=\"./demos/tree-editing\"]","link":"docs/tree-source-data-editing#editing-tree-cells","depth":2},{"header":"Editing Tree Groups","text":"Editing Tree Groups To enable group cell editing, replace the LyteNyte Group column with a custom group column. First, set rowGroupColumn to false to prevent the grid from creating an automatic group column. Next, add a custom group column to your definitions. Since you define this column, set the editable and editRenderer properties to enable editing. The demo below replaces the Group column with a custom Files column. Double-click a file name cell to edit it. :::note The demo sorts rows by Size. Renaming a group can change the branch order when the underlying JavaScript object is updated. Sorting by Size ensures the rows maintain a stable order. ::: ::demo[Tree Group Editing=\"./demos/tree-editing-group\"]","link":"docs/tree-source-data-editing#editing-tree-groups","depth":2},{"header":"Data Object","text":"Data Object Use the useTreeDataSource hook to render a nested object as a row set. The data property must be a JavaScript object. Pass these functions to control how the hook creates rows from the source object: rowValueFn: Returns the data associated with a specific row.rowChildrenFn: Returns an array of objects used to generate child rows. The demo below uses objects with a children key to define the hierarchy. ::demo[Custom Keys=\"./demos/basic-keys\"]","link":"docs/tree-source-data#data-object","depth":2},{"header":"Root Rows","text":"Root Rows Define rowRootFn to specify the initial root rows. This function is required when using a wrapper object where the top-level property should not appear as a row. The following demo uses rowRootFn to bypass a root property and return the nested contents as the starting rows. ::demo[Root Rows=\"./demos/basic-root\"] In this example, the grid ignores the top-level root property in the data. The rowRootFn function returns the children property of the root object as an array of entries. Each entry becomes a row, and the grid branches from there.","link":"docs/tree-source-data#root-rows","depth":3},{"header":"Filtering Tree Rows","text":"Filtering Tree Rows Set the filter property on the useTreeDataSource hook to filter rows created from the object provided to the data property of the tree data source. The filter property accepts a predicate function that receives each value from the object provided to data. If the function returns true, the value and any of its children are kept; otherwise, they are removed. In this demo, hidden files are defined as files whose names start with a ., like .env. Use the Reveal Hidden Files switch to toggle their visibility. ::demo[Tree Row Filtering=\"./demos/tree-filtering\"]","link":"docs/tree-source-filtering#filtering-tree-rows","depth":2},{"header":"Basic Tree","text":"Basic Tree The useTreeDataSource hook transforms a nested JavaScript object into a row data source. Assign the object to the data property of the useTreeDataSource hook. The example below shows one such configuration: By default, the hook traverses object properties and creates a branch whenever a property value is itself another object. The demo below renders a tree from a standard JavaScript object. While the tree data source generates the rows, you must still define the columns. Since this structure allows recursion at any level, the data source treats all rows as group rows. ::demo[Basic Tree=\"./demos/basic-tree\"] The remaining guides in this tree data section cover advanced configurations, including custom row creation and data editing.","link":"docs/tree-source-overview#basic-tree","depth":2},{"header":"Top Pinned Rows","text":"Top Pinned Rows Pin rows to the top of the viewport by assigning an array of RowLeaf or RowAggregated nodes to the topData property of the useTreeDataSource hook. The demo below shows a tree data source with rows pinned to the top. ::demo[Tree Data Top Pinned Rows=\"./demos/tree-pin-top\"]","link":"docs/tree-source-pinning#top-pinned-rows","depth":2},{"header":"Bottom Pinned Rows","text":"Bottom Pinned Rows Pin rows to the bottom of the viewport by assigning an array of RowLeaf or RowAggregated nodes to the botData property. Rows assigned to botData remain fixed at the bottom edge while the rest of the tree scrolls. The demo below shows a tree data source with rows pinned to the bottom. ::demo[Tree Data Bottom Pinned Rows=\"./demos/tree-pin-bottom\"]","link":"docs/tree-source-pinning#bottom-pinned-rows","depth":2},{"header":"Sort Function","text":"Sort Function In the tree data source, the sort function compares two row nodes. Each node is a group row node unless it's pinned. The demo below shows basic sorting. Click Sort: Size to sort rows by Size in descending order. ::demo[Tree Sort Function=\"./demos/tree-sorting\"]","link":"docs/tree-source-sorting#sort-function","depth":2},{"header":"Sort Dimensions","text":"Sort Dimensions You can set the sort property to an array of DimensionSort objects. Each dimension sort uses the field property of a column to compare rows. A dimension can be applied to sort rows in ascending or descending order. Since the hook receives an array of DimensionSort objects, LyteNyte Grid can perform a multi-way sort. The tree data source applies the comparators in the order they appear in the provided array, stopping when a comparator result is not 0. The demo below shows dimension sorting. Click a column header to sort. To sort by multiple columns, hold Control or Command and click additional headers. ::demo[Tree Sort Dimensions=\"./demos/tree-sorting-dimensions\"]","link":"docs/tree-source-sorting#sort-dimensions","depth":2},{"header":"v2.0.2","text":"v2.0.2 Mar 8, 2026 Fixed Smart Select not displaying selection list when open.Fixed Column Manager not correctly rendering split column groups.Fixed multi range selection deselecting a larger selection rectangle when only a single cell is deselected.","link":"docs/changelog/latest#v202","depth":2},{"header":"v2.0.0","text":"v2.0.0 Feb 17, 2026 V2 Release Grid LyteNyte Grid v2 improves on v1 in almost every aspect. The primary focus of this release is the grid's developer experience. LyteNyte Grid is designed to fit your application, not force your application to fit the grid. v2 is smaller, faster, more lightweight, and more capable than v1. It achieves these improvements in three key ways: Removal of the useLyteNyte hook in favor of a completely prop-driven grid.Introduction of API and column extensions.A predefined headless configuration. In addition, v2 includes many minor and quality-of-life improvements, along with significant enhancements to existing features.","link":"docs/changelog/latest#v200","depth":2},{"header":"New Grid Features","text":"New Grid Features Use the cellSelectionExcludeMarker grid property to prevent selection of the marker column.Use the cellSelectionMaintainOnNonCellPosition grid property to prevent clearing cell selection when focus moves to a non-grid element.The columnMoveDragPlaceholder and columnGroupMoveDragPlaceholder properties allow you to specify a custom drag indicator when moving columns or column groups.The columnGroupRenderer property allows you to provide a custom React component to render column groups.Use the events property to define event handlers for any standard HTML event on any part of the grid, such as cells, rows, or headers. Event handlers receive additional contextual information.Use the styles property to apply inline styles and classes to grid elements.Use the rowAlternateAttr property to disable the row-alternating data attribute.Use the rowDropAccept property to allow rows from other grids to be dragged into the current grid.Use the ref property to obtain a reference to the grid API.Use the slotShadows property to render the new Viewport Shadows component.Use the slotViewportOverlay and slotRowsOverlay properties to render custom overlay components.editMode now supports full row editing.The onColumnMoveOutside event detects when a column is dragged outside the grid.Pivot mode now supports a Grand Totals row.Pivot columns can now be processed and alternated before the grid renders them.Column Groups can now be used to resize all columns under the group if the child columns are resizable.The pivotModel now supports label filtering.You can now apply label filters to the client row source.You can now apply having filters (post-grouping filters) to the client row source.All grid row sources now directly support adding and deleting rows.Client row groups can now be flattened when a group has a single leaf child.Row selection now supports two modes:Isolated row selection, where each row maintains its own selection state.Linked row selection, where a row group's selection state links to the selection state of its children.You can now exclude individual rows from the set of selectable rows.The client row source can now suppress automatic expansion to the leaf row.The client row source now supports unbalanced row groups with varying levels of depth. This capability replaces the existing tree data source and satisfies both use cases.The tree data source now accepts object-based data instead of array-based data.Render arbitrary objects.Edit object data.Build advanced tree views.New components and utilities:A headless SelectAll component for selecting or deselecting all rows.A RowGroupCell component for managing row group expansion.A simple TreeView component that renders a specially configured version of LyteNyte Grid.A SmartSelect component that you can use as a custom dropdown, combobox, or multi-combobox.A Dialog component for grid-specific dialogs or general application dialogs.A Popover component for grid features or general popover usage.A Menu component for general-purpose dropdown menus.","link":"docs/changelog/latest#new-grid-features","depth":3},{"header":"Grid Property Changes","text":"Grid Property Changes The cellSelectionExcludeMarker property has been added. This property prevents marker column selection.The onCellSelectionChange property has been added. This property handles cell selection updates.The colScanDistance property has been removed. The grid now determines the appropriate scan distance automatically.The rowFullWidthPredicate and rowFullWidthRenderer parameters now receive the grid API instead of the grid state object.The sortModel, filterModel, filterInModel, rowGroupModel, and aggModel properties have been removed. These models now live on the grid's row data sources. You can now define custom models.The rowGroupColumn property now accepts false to disable it. The rowGroupDisplayMode property has been removed.The rowGroupDefaultExpansion and rowGroupExpansion properties now live on the row data source.The cellRenderers, floatingCellRenderers, headerCellRenderers, and editRenderers properties have been removed. These properties are no longer required.rowSelectionActivator now uses \"single-click\" instead of \"single\".The rowDetailRenderer now receives the grid API instead of the grid state object.The editCellMode property has been renamed to editMode.The editRowValidatorFn now receives the updated edit parameters.The columnMarkerEnabled property has been removed. Enable the marker column through the marker column definition.The rowDataSource property has been renamed to rowSource.The rowSelectedIds property has been removed. The row data source now manages row selection.The rowSelectChildren property has been removed. Use linked row selection instead.The quickSearch and quickSearchSensitivity properties have been removed. Define quick search at the row source level.The columnPivotMode and columnPivotModel properties have been removed. Handle column pivoting at the row source level.The dialogFrames and popoverFrames properties have been removed. You can now trigger dialogs and popovers without these properties.","link":"docs/changelog/latest#grid-property-changes","depth":3},{"header":"Column Property Changes","text":"Column Property Changes The headerRenderer, cellRenderer, floatingCellRenderer, and editRenderer properties no longer accept a string value for a pre-registered renderer. Resolve the renderer before providing the column definitions to the grid.The quickSearchIgnore property has been removed. Quick search is now opt-in.The uiHints property has been removed. Column extensions now replace this functionality.The resizable property has been added. It indicates whether the column can be resized.The movable property has been added. It indicates whether the column can be moved.The editOnPrintable property has been added. It determines whether a cell edit begins when a printable character is pressed.The editMutateCommit property has been added. This callback function mutates edit data before the grid updates the row. All function parameter types on the Column type now reflect the updated grid API and Column extension system introduced in v2. The colSpan and rowSpan functions now receive api, column, row, rowIndex, and colIndex. The grid parameter has been removed.The field function now receives row data to compute the value. The grid, column, and data parameters have been removed. These parameters are not required in v2 and are not available in every execution context.The headerRenderer function now receives api and column. The grid parameter has been removed.The cellRenderer function now receives api, column, rowIndex, colIndex, selected, indeterminate, detailExpanded, editData, and layout. The grid, rowSelected, rowPin, and rowIndeterminate parameters have been removed.The editRenderer function now receives api, row, column, rowIndex, colIndex, editValue, changeValue, editData, editValidation, changeData, commit, cancel, and layout. The grid parameter has been removed.The editable function now receives api instead of grid.The editSetter parameters have been reworked to provide data change types and the grid API instead of the grid state object.","link":"docs/changelog/latest#column-property-changes","depth":3},{"header":"API Property Changes","text":"API Property Changes The cellSelection method has been added to retrieve the current grid selection state.The xPosition$ and yPosition$ properties have been added to provide reactive access to the grid's coordinate positions.The viewport$ property has been added to provide reactive access to the grid's viewport state.The columnView method has been added to retrieve a description of the currently visible columns.The rowDetailIsExpanded property has been renamed to rowDetailExpanded.The rowDetailRenderedHeight property has been renamed to rowDetailHeight.The rowIsAggregated method has been added to determine whether a row is aggregated.The rowGroupIsExpanded method has been renamed to rowIsExpanded.The rowIsExpandable method has been added to determine whether a row can be expanded.The rowView method has been added to retrieve a description of the current row view.The exportDataRect method has been renamed to exportData.The editUpdate method has been split into two focused methods: editUpdateRows and editUpdateCell.The useRowDrag hook now provides streamlined drag parameters.The columnIndex method has been removed. Use the columnView method instead.The sortForColumn method has been removed. A column’s sort index now derives from the defined sort model.The rowGroupColumnIndex method has been removed. The grid now creates only a single row group column.The rowGroupApplyExpansions method has been removed. The grid no longer requires this method.The eventAddListeners, eventRemoveListener, and eventFire methods have been removed. LyteNyte Grid no longer maintains a custom event system.The exportCsv and exportCsvFile methods have been removed. Use the more flexible exportData method instead.The dialogFrameOpen, dialogFrameClose, popoverFrameOpen, and popoverFrameClose methods have been removed. The grid no longer manages dialog or popover state.The rowSelect method can now select all rows. The rowSelectAll method has been removed. LyteNyte Grid now extends the Grid API with additional methods and properties based on the provided row source. The following properties from v1 have changed or are now supplied by the row source: rowById is now provided by the row source.rowByIndex is now provided by the row source.","link":"docs/changelog/latest#api-property-changes","depth":3},{"header":"Client Row Source Changes","text":"Client Row Source Changes The useClientRowDataSource hook has been renamed to useClientDataSource. The useClientRowDataSourcePaginated hook has been removed. You can use useClientDataSource for paginated client data, but this approach is not recommended. When all data exists on the client, pagination introduces unnecessary complexity and is generally an anti-pattern. The useClientDataSource hook now treats the row source as a derived value based on the provided properties. As a result, the hook behaves like useMemo rather than useState. bottomData has been renamed to botData.reflectData has been removed. This property is no longer required.rowIdLeaf has been renamed to leafIdFn.rowIdBranch has been renamed to groupIdFn.transformInFilterItem has been removed. The client source no longer owns the data, and filter items can now be derived directly from the data. The client row source is now responsible for: Column pivoting and pivot mode.Managing row group expansion state.Sorting, filtering, and aggregating row nodes.Managing row selection state.Creating, updating, and deleting rows. In v1, the grid managed these responsibilities and indirectly delegated them to the client source. This design required the grid to initialize before you could safely use client source state. In v2, the grid removes this indirection. The client source is immediately usable, regardless of whether the grid has mounted.","link":"docs/changelog/latest#client-row-source-changes","depth":3},{"header":"Server Row Source Changes","text":"Server Row Source Changes The useServerDataSource hook is more capable in v2 while maintaining the same level of simplicity as v1. dataFetcher has been renamed to queryFn. The grid no longer passes built-in data models to this function. You can now define and manage your own custom models.dataFetchExternals has been renamed to queryKey. The queryKey property is now strongly typed through a generic parameter.dataColumnPivotFetcher has been removed. Fetch pivot columns directly inside your query handler.dataInFilterItemFetcher has been removed. Fetch filter items directly inside your query handler.cellUpdate has been renamed to onRowDataChange to keep prop names consistent across row data sources.cellUpdateOptimistically has been renamed to rowUpdateOptimistically. The server data source now also manages: Row group expansion state.Row selection state. The server data source can now: Delete rows.Add rows.","link":"docs/changelog/latest#server-row-source-changes","depth":3},{"header":"Tree Row Source Changes","text":"Tree Row Source Changes The tree data source from v1 has been removed. Use the client data source as a replacement. v2 introduces a new, more advanced tree data source designed specifically for object-based data. Because this implementation is entirely new, there is no direct migration path or property mapping from v1. See the Tree Overview guide for more details.","link":"docs/changelog/latest#tree-row-source-changes","depth":3},{"header":"Components Changes","text":"Components Changes The SortManager and ListBox components have been removed. You can recreate their functionality using the new components provided by LyteNyte Grid. The PillManager component replaces the ListBox component. PillManager supports reordering and activating pill items. The ColumnManager component has been simplified and is now more opinionated in its behavior. You can use it as a drop-in column manager. For more flexible and configurable tree structures, use the new TreeView component available in LyteNyte Grid.","link":"docs/changelog/latest#components-changes","depth":3},{"header":"Theme Token Changes","text":"Theme Token Changes LyteNyte Grid v2 introduces more clearly defined theme tokens while preserving the existing color palette. The theme CSS is now split into separate files. See the Grid Theming guide for more details.","link":"docs/changelog/latest#theme-token-changes","depth":3},{"header":"Migration from v1.x","text":"Migration from v1.x This section provides a high-level overview of migrating from v1 to v2 of LyteNyte Grid. If you have questions, open an issue on GitHub. There are two primary changes required to migrate to v2: The steps above cover the primary migration changes. Review the changelog for property renames and removals. TypeScript should surface most required updates. Remove Effect Code v2 is fully prop-driven. Remove any synchronization logic that mutates grid state through useEffect. For example: Replace it with: Replacing the Predefined Models In v1, LyteNyte Grid predefined filter, sort, and group models. In v2, your code defines these models dynamically. See the following guides for the updated approach: FilteringSortingRow Grouping","link":"docs/changelog/latest#migration-from-v1x","depth":3},{"header":"v0.9.4 Server Data Source Reset","text":"v0.9.4 Server Data Source Reset v0.9.4 - June 30, 2025 v0.9.4 addresses an issue where changes to column definitions incorrectly triggered a reset of the loaded row data in the server-side data source. This behavior stemmed from LyteNyte Grid validating the sort and filter models during column updates, which led the server data source to mistakenly interpret these validations as model changes. As a result, it unnecessarily reloaded the row data. v0.9.4 fixes the issue, row data will now only be reset when the sort or filter models have actually changed.","link":"docs/changelog/v094#v094-server-data-source-reset","depth":2},{"header":"v0.9.3 Turbopack CSS fix","text":"v0.9.3 Turbopack CSS fix v0.9.3 - June 27, 2025 v0.9.3 resolves CSS selector compatibility issues to enable proper bundling with Turbopack, ensuring seamless integration with Next.js applications.","link":"docs/changelog/v094#v093-turbopack-css-fix","depth":2},{"header":"v0.9.2 Minor Performance Enhancement","text":"v0.9.2 Minor Performance Enhancement v0.9.2 - June 26, 2025 v0.9.2 improves internal caching optimizations for grid field calculations. While these improvements aren't visible to users, they make LyteNyte Grid more responsive and efficient under the hood.","link":"docs/changelog/v094#v092-minor-performance-enhancement","depth":2},{"header":"v0.9.1 Fixing Safari","text":"v0.9.1 Fixing Safari v0.9.1 - June 19, 2025 v0.9.1 resolves a browser-specific repaint issue that occurred only in the Safari browser on macOS. Previously, when hovering over rows in the LyteNyte Grid, the highlight effect was applied by modifying a element. In Safari, this caused the entire grid to repaint instead of just the affected rows. This has now been corrected: the hover highlight is applied directly to each cell's style attribute and removed when the hover ends, preventing unnecessary full-grid repaints.","link":"docs/changelog/v094#v091-fixing-safari","depth":2},{"header":"Introducing LyteNyte Grid!","text":"Introducing LyteNyte Grid! v0.9.0 - May 18, 2025 Release Version 0.9.0 marks the initial public release of LyteNyte Grid, a lightning-fast JavaScript data grid tailored to meet your enterprise-level data management needs. LyteNyte Grid is available in two editions: Core and PRO. Today's release introduces both editions to the public for the first time. LyteNyte Grid Core includes essential features you'd expect from a powerful JavaScript data grid, such as column moving and resizing, row grouping and aggregation, and row selection.LyteNyte Grid PRO extends the Core edition with enterprise-grade features like column pivoting and server-side data loading, providing robust support for complex data scenarios. A full working demo is available on our demo page. To view a detailed feature comparison, visit our pricing page. For quick setup and usage, check out our Getting Started Guide. We've also published a series of articles to help you better understand LyteNyte Grid and our licensing approach: Introducing LyteNyte Grid: Comprehensive overview of LyteNyte Grid.Understanding LyteNyte Grid PRO Redistribution: Clear explanation of LyteNyte Grid PRO’s redistribution policy.Support the 1771 Technologies Way: Insight into our support philosophy and what to expect when purchasing a license.Who's in Scope with LyteNyte Grid PRO: Details about licensing requirements and who needs a LyteNyte Grid PRO license.","link":"docs/changelog/v094#introducing-lytenyte-grid","depth":2},{"header":"v1.0.18","text":"v1.0.18 v1.0.18 - Oct 27, 2025 Adds LyteNyte Grid to the shadcn registry. See the installation guide to get started with LyteNyte Grid in shadcn. Introduces three new prebuilt themes for LyteNyte Grid: shadcn light, shadcn dark, and cotton candy. See the demo for live examples, or refer to the theming guide for instructions on using the prebuilt themes.","link":"docs/changelog/v1#v1018","depth":2},{"header":"v1.0.17","text":"v1.0.17 v1.0.17 - Oct 17, 2025 Enhanced useServerDataSource interfaces. This patch adds several useful server data loading methods available from the returned server data source hook: refresh: Triggers a new data fetch for the current view. Enables easy polling.requestForGroup: Returns the DataRequest for a given group row.requestForNextSlice: Returns the DataRequest for the next slice to aid optimistic data fetching.requestsForView: Returns the DataRequests for the current view.seenRequests: A writable set that lets users clear or add request IDs to LyteNyte Grid's seen requests cache. Additionally, the useServerDataSource hook now accepts an optional dataFetchExternals dependency array. Use this to make the server data source depend on external React state. See the server data loading guides for full details on using the server data source in LyteNyte Grid.","link":"docs/changelog/v1#v1017","depth":2},{"header":"Other Fixes","text":"Other Fixes Fixed full-width rows in Firefox not being positioned correctly in the view.","link":"docs/changelog/v1#other-fixes","depth":3},{"header":"v1.0.16","text":"v1.0.16 v1.0.16 - Oct 08, 2025 Bug fixes for Firefox Fixed columns pinned to the end of the grid not staying in place while scrolling.Fixed master-detail rows causing overflow.Fixed column header dragging for swapping columns.","link":"docs/changelog/v1#v1016","depth":2},{"header":"v1.0.0","text":"v1.0.0 v1.0.0 - Aug 19, 2025 V1 Release Grid LyteNyte Grid v1.0.0 marks the official stabilization of the LyteNyte Grid API. This release incorporates lessons learned from earlier iterations and developer feedback, with a strong focus on flexibility. At a glance, v1.0.0 delivers: Stabilized public interfaces, going forward, only minor changes are expected.Headless rendering interfaces, prior versions used a “black-box” component, which made extensions difficult. The new headless API is intuitive for React developers and makes future improvements easier without breaking changes.Improvements across the board, better memory usage, higher performance, and reduced bundle size.","link":"docs/changelog/v1#v100","depth":2},{"header":"Migration from v0.9.x","text":"Migration from v0.9.x Column Definition Changes headerName → nameheaderAutosizeFn → autosizeHeaderFncellAutosizeFn → autosizeCellFncellAutosizeFn removed. Instead, define only the columns you want to autosize in the API.type now accepts arbitrary strings and includes support for datetime. The complex type has been removed.aggFnsAllowed and aggFnDefault moved to uiHints. Any column can now be aggregated without explicit configuration.hidable removed. All columns are now hidable.Editing props consolidated. cellEditPredicate, cellEditParser, cellEditUnparser, cellEditProvider, cellEditParams, and cellEditRowUpdater are replaced by editRenderer, editSetter, and editable. See our cell editing guide.sortable moved to uiHints. All columns are sortable when added to the sort model.sortCycle and sortComparator removed. Define custom logic in your own code as needed.inFilterLabelFormatter removed. Now handled at the row data source level.inFilterField removed. no longer needed.quickSearchField removed. no longer needed.groupVisibility simplified to always, open, or closed.rowGroupable moved to uiHints. Any column can now be grouped when added to the group model.rowGroupField removed. The row group model now accepts a field object directly, making it more flexible.measureFnsAllowed and measureFnDefault removed. The pivot model now accepts an aggregation model.resizable and movable moved to uiHints. LyteNyte State aggFns removed. Aggregations are now defined at the row data source level.autosizeDoubleClickHeader → columnDoubleClickToAutosizecellEditPointerActivator → editClickActivatorcellEditProviders → editRendererscolumnHeaderHeight → headerHeightcolumnHeaderRenderers removed. unnecessary in headless mode.columnGroupHeaderHeight → headerGroupHeightcolumnGroupHeaderRenderer removed. unnecessary in headless mode.columnGroupStickHeaders removed. unnecessary in headless mode.columnGroupIdDelimiter → columnGroupJoinDelimiter (clearer naming).columnGroupExpansionState → columnGroupExpansionscolumnSpanScanDistance → colScanDistancerowDetailMarker removed. unnecessary.rowDetailEnabled removed. Row detail is always available via detail expansion state.Row dragging properties (rowDragEnabled, rowDragMultiRow, rowDragExternalGrid, rowDragPredicate) removed. Use grid.api.useRowDrag instead.rowGroupAutoApplyAggDefault removed. unnecessary.rowGroupColumnTemplate → rowGroupColumnPagination properties (paginate, paginatePageSize, paginateCurrentPage) removed. Controlled by row data source.rowSelectionCheckbox removed. unnecessary.rowSelectionPointerActivator → rowSelectionActivatorrowSelectionPredicate removed. Row selection can now be prevented via the rowSelectBegin event.rowSelectionSelectChildren → rowSelectChildrenrowSelectionMultiSelectOnClick removed. unnecessary.sortComparatorFns removed. Use custom comparators directly in sortModel.Overlay and menu-related properties (overlayToShow, overlays, columnMenuRenderer, contextMenuRenderer, contextMenuPredicate, panelFrames, panelFrameButtons, menuFrames) removed. unnecessary in headless mode.Pivot-related properties refactored. columnPivotModel now holds the complete pivot configuration.filterQuickSearch → quickSearchfilterModel split into FilterModel (simple filters) and FilterInModel (set filters).measureModel removed. Now defined in columnPivotModel.treeData removed. unnecessary. Grid API autosizeMeasure removed. Use the new measureText function exported by LyteNyte Grid packages.Cell editing events:cellEditBegin → editBegincellEditEnd → editEndcellEditIsActive → editIsCellActiveMost cellEdit* APIs removed. Use events (editBegin, editEnd) instead.columnField now takes (column, row) instead of (row, column).columnGroupToggle → columnToggleGroupcolumnUpdate now handles both single and multiple updates.Replaces columnUpdateMany, columnResize, and columnResizeMany.Column movement APIs simplified into columnMove.Numerous columnIs* and columnSort* helpers removed. No longer necessary.Navigation APIs consolidated. Use focusCell instead of navigate*.navigateScrollIntoView → scrollIntoViewRow refresh, reload, and data replacement APIs removed. Handled by row data source.Selection APIs simplified:rowSelectionGetSelected → rowSelectedrowSelectionSelect → rowSelectrowSelectionSelectAll → rowSelectAllUndo/redo and legacy menu/frame APIs removed.Pivot APIs consolidated into the columnPivotModel. Migrating to Headless Components Replace: With:","link":"docs/changelog/v1#migration-from-v09x","depth":3},{"header":"Grid Public Interface","text":"Grid Public Interface LyteNyte Grid's public API has six sections: Grid Props: Properties passed to the grid component to configure and customize grid behavior.Grid API: Methods exposed by the grid to allow imperative interaction.Column: Define column specifications and what the grid displays.Row: Define row types that dictate how the grid retrieves and renders data for cells.Layout: Objects used to assemble the grid when running in headless mode.Grid Types: Auxiliary types used by the grid's functions, methods, and properties.","link":"docs/reference#grid-public-interface","depth":2},{"header":"Grid Row Source","text":"Grid Row Source The row source is a separate interface that the grid uses to query and interact with row data. LyteNyte Grid provides several built-in row sources: Row Source: An overview of the row data source interface.Client Row Source: A row source for data that exists on the client.Server Row Source: A row source for loading data in slices from the server.Tree Row Source: A row source for rendering and editing hierarchical, object-based data.","link":"docs/reference#grid-row-source","depth":2},{"header":"Grid Utility Functions","text":"Grid Utility Functions LyteNyte Grid provides a set of utility functions that enhance the grid's functionality: Piece utility: Creates stable references from unstable state sources that can be shared with the grid and other components.Utility Functions: Includes other small utility functions, such as equal, to simplify building advanced use cases within the grid.","link":"docs/reference#grid-utility-functions","depth":2},{"header":"Reference vs. Guides","text":"Reference vs. Guides Each reference page provides descriptions and type information required to use the public API. Use this API reference section as a quick lookup for method and function details, and consult the Guides to learn how to implement LyteNyte Grid in practice.","link":"docs/reference#reference-vs-guides","depth":2},{"header":"Extending Columns","text":"Extending Columns In addition to the properties LyteNyte Grid supports out of the box, you can extend a column definition with additional properties specific to your application. The code below shows a brief example:","link":"docs/reference/column#extending-columns","depth":2},{"header":"Getting the API","text":"Getting the API LyteNyte Grid provides the API as a prop to all renderers and event callbacks exposed by the grid. If you need to use the API in other parts of your application, provide the grid with a ref prop to access the API directly.","link":"docs/reference/grid-api#getting-the-api","depth":2},{"header":"Extending the API","text":"Extending the API You can extend the LyteNyte Grid API with custom properties to add new capabilities and simplify integration of the grid into your application. The code below shows a basic example. See the API Extension guide for more details.","link":"docs/reference/grid-api#extending-the-api","depth":2},{"header":"Grid API Properties","text":"Grid API Properties The properties listed below are grouped into sections, but they are all available on the same API instance. :::note LyteNyte Grid's API is further extended by the row source provided to the grid. In addition to the properties listed below, the row source properties are also available on the API instance. See the Row Source reference for more details. :::","link":"docs/reference/grid-api#grid-api-properties","depth":2},{"header":"Properties","text":"Properties Provide these props to the Grid component to configure the grid.","link":"docs/reference/grid-props#properties","depth":2},{"header":"Row Layouts","text":"Row Layouts Two layout types are necessary to describe a row's layout: Full Width Layout: Used for full width rows.Cell Layout: Used for rows with individual cells. The LayoutRow type defines the row layout as a union of the cell and full-width layout types.","link":"docs/reference/layout#row-layouts","depth":2},{"header":"Header Layouts","text":"Header Layouts Three layout types are necessary to describe a header's layout: Header group cellHeader cellFloating header cell The LayoutHeader type defines the header layout as a union of these types.","link":"docs/reference/layout#header-layouts","depth":2},{"header":"Row Node","text":"Row Node The RowNode type represents a union of the three row types. While this section provides a reference for each row type, grid APIs typically use the RowNode type rather than an individual row type. For more details on row nodes, see the Row Overview guide.","link":"docs/reference/row#row-node","depth":2},{"header":"Client Source Params","text":"Client Source Params To create a client row source, use the useClientDataSource hook exported by LyteNyte Grid. This hook accepts parameters that configure the client source view.","link":"docs/reference/client-row-source#client-source-params","depth":2},{"header":"Client Source API Additions","text":"Client Source API Additions The client row source extends the grid API with the following additional properties.","link":"docs/reference/client-row-source#client-source-api-additions","depth":2},{"header":"Server Source Params","text":"Server Source Params To create a server row source, use the useServerDataSource hook exported by LyteNyte Grid. This hook accepts parameters that configure the server source view.","link":"docs/reference/server-row-source#server-source-params","depth":2},{"header":"Server Source API Additions","text":"Server Source API Additions The server row source extends the grid API with the following additional properties.","link":"docs/reference/server-row-source#server-source-api-additions","depth":2},{"header":"Tree Source Params","text":"Tree Source Params To create a tree row source, use the useTreeDataSource hook exported by LyteNyte Grid. This hook accepts parameters that configure the tree source view.","link":"docs/reference/tree-row-source#tree-source-params","depth":2},{"header":"Tree Source API Additions","text":"Tree Source API Additions The tree row source extends the grid API with the following additional properties.","link":"docs/reference/tree-row-source#tree-source-api-additions","depth":2},{"header":"Use Piece","text":"Use Piece The piece utility is a hook exported by LyteNyte Grid. The basic usage is shown below: The usePiece hook creates a Piece value. The Piece value updates internally whenever the count value changes. However, the count$ reference itself is stable and never changes. As a result, passing count$ to context values, effects, or as props to a memoized component will not cause re-renders. You can retrieve the internal value using one of the following methods: count$.get: Returns the current value non-reactively. Use this method in event callbacks or effects.count$.useValue: Subscribes to the value reactively. This method is a hook and must follow the rules of hooks. Calling useValue causes the component in which it is used to re-render whenever the count value changes. The count$.useValue hook also supports a selector function. When you provide a selector, the component only re-renders when the selector's return value changes.","link":"docs/reference/piece-utility#use-piece","depth":2},{"header":"Writable Piece","text":"Writable Piece If you provide a setter to the usePiece hook, the returned Piece becomes writable. You can call set to update the value. Internally, the set method calls the setCount dispatch function returned by useState with the value 23.","link":"docs/reference/piece-utility#writable-piece","depth":3},{"header":"Extending the Grid's API","text":"Extending the Grid's API The primary use case for the usePiece hook is to make it easier to extend the grid's API reactively. LyteNyte Grid creates its API once and maintains a stable reference. Because of this, updates to reactive values are not reflected in the API by default. The usePiece hook wraps a reactive value in a stable reference that can be passed to the grid's API as an extension, as shown below. See the API Extension guide for more details.","link":"docs/reference/piece-utility#extending-the-grids-api","depth":2},{"header":"measureText","text":"measureText The measureText function estimates the width, in pixels, of a string. It uses an offscreen canvas to compute the result.","link":"docs/reference/utility-functions#measuretext","depth":2},{"header":"moveRelative","text":"moveRelative The moveRelative function reorders items in an array. This function is most useful for drag-and-drop operations.","link":"docs/reference/utility-functions#moverelative","depth":2},{"header":"equal","text":"equal The equal function performs fast deep equality comparisons. This function is a customized fork of React Fast Compare.","link":"docs/reference/utility-functions#equal","depth":2},{"header":"arrayShallow","text":"arrayShallow The arrayShallow function compares two arrays for shallow equality. This comparison is faster when you only care about reference equality within the array.","link":"docs/reference/utility-functions#arrayshallow","depth":2},{"header":"virtualFromXY","text":"virtualFromXY The virtualFromXY function returns a virtual anchor that can be used with the popover component provided by LyteNyte Grid.","link":"docs/reference/utility-functions#virtualfromxy","depth":2},{"header":"Filtering With An Expression","text":"Filtering With An Expression A filter expression is a standard expression that evaluates to a boolean value. The expression defines the predicate, and the evaluator checks whether each row satisfies that predicate. The example below demonstrates filter expressions in action. You can perform any type of comparison on the columns. The expression is preloaded with Gender == \"Male\" && Quantity > 10, which translates to \"any row with a gender of 'Male' and a quantity greater than 10.\" ::demo[Expression Filters=\"./demos/expression-filter\"] To handle expression filters for columns, configure the Evaluator with a custom plugin provided by LyteNyte Grid: createResolvedIdentifierPlugin. This function returns a plugin that treats specific identifiers as values. The code is shown below: Each column evaluates per row, so the evaluator must resolve the column value for a given row. However, you do not want to treat columns as functions in the expression (for example, the user should be able to write Gender instead of Gender() in the expression). The plugin performs this substitution at the AST level, so when a user writes Gender, the evaluator resolves it to the correct cell value. See the demo code for the full usage.","link":"docs/expression-filters#filtering-with-an-expression","depth":2},{"header":"Filter Expressions Considerations","text":"Filter Expressions Considerations Filter expressions evaluated on the client are more expensive than hard-coded filter functions. This is because the evaluator processes the expression from an AST, which adds an extra computation step. In return, you gain more flexibility in the types of filters users can create. Evaluating an expression AST on the client works well for client-side data, but it is less suitable for server-side data. In these cases, you can serialize the AST and send it to the server. The AST produced by the Evaluator is serializable unless you provide a custom plugin that generates nodes that cannot be converted to JSON.","link":"docs/expression-filters#filter-expressions-considerations","depth":2},{"header":"Extending Expression Syntax","text":"Extending Expression Syntax LyteNyte Grid provides the Evaluator class, which accepts an array of plugins when you instantiate it. These plugins extend the evaluator's capabilities and allow it to handle a wider range of expressions. A Plugin is any object that conforms to the interface shown below. You only need to implement the parts of the interface that your plugin requires. Each method corresponds to a stage in the evaluator's flow (tokenizing, parsing, and evaluating). Plugins are best understood through examples. The following sections provide examples you can use as a reference when building your own plugins.","link":"docs/expression-plugins#extending-expression-syntax","depth":2},{"header":"AST Enhancement Example","text":"AST Enhancement Example The code below shows createResolvedIdentifierPlugin. This plugin treats specific identifiers as implicit function calls during evaluation. See the Expression Filter guide for a demonstration.","link":"docs/expression-plugins#ast-enhancement-example","depth":3},{"header":"Boolean Support Example","text":"Boolean Support Example The plugin below adds boolean support to the expression syntax. It is used by the standard plugins.","link":"docs/expression-plugins#boolean-support-example","depth":3},{"header":"Expression Flow","text":"Expression Flow LyteNyte Grid exports the Evaluator class. To get started, import this class as shown below: The Evaluator class evaluates a string expression and returns a value. For example: The demo below allows you to perform basic mathematical calculations using the evaluator. ::demo[Basic Evaluator=\"./demos/evaluate-basics\"] The expression evaluator follows the same flow: Tokenization: The evaluator parses the expression and breaks it into tokens. The ExpressionEditor component uses these tokens to provide syntax highlighting.Parsing: The evaluator converts the tokens into an Abstract Syntax Tree (AST). The AST represents the calculation.Evaluation: The evaluator processes the AST to produce the result. If you are familiar with programming language development, this flow will be familiar. The Evaluator class provides methods to access each stage: Evaluator.run evaluates an expression string and returns the result.Evaluator.tokens or Evaluator.tokensSafe return the tokenized output.Evaluator.ast returns the AST.","link":"docs/expressions-overview#expression-flow","depth":2},{"header":"Expression Plugins and Context","text":"Expression Plugins and Context The Evaluator class uses a plugin-based architecture. By default, it behaves like a basic calculator, but plugins allow you to define a full domain-specific language. LyteNyte Grid provides a set of standard plugins for common operations such as boolean logic, string handling, comparisons, and object membership. The Evaluator.run method accepts a context object that provides values for identifiers used in the expression. The example below demonstrates this using the standardPlugins export, which provides support for a JavaScript-like expression language. It also passes a context object that the expression uses during evaluation. ::demo[Standard Plugins=\"./demos/evaluate-standard-plugins\"] In the demo, the context can include primitive values, functions, objects, and any other values the evaluator needs to compute the result.","link":"docs/expressions-overview#expression-plugins-and-context","depth":2},{"header":"Expression Editor","text":"Expression Editor Use the ExpressionEditor component to create an input with syntax highlighting and IntelliSense. The ExpressionEditor is a single-line input that represents an expression. It requires a tokenizer and a string expression. The ExpressionEditor completion subcomponents provide contextual IntelliSense. To enable this, pass a completion provider as a prop. A completion provider is a function that returns a set of completions based on the current cursor position. LyteNyte Grid provides a helper for creating completion providers when completions derive from a context object. The demo below shows an ExpressionEditor that uses completion list components. Try typing user. in the editor. The completion list appears. Completion lists are optional, but they improve usability. ::demo[Expression Completions=\"./demos/expression-completions\"]","link":"docs/expressions-overview#expression-editor","depth":2},{"header":"Completion Provider","text":"Completion Provider The createCompletionProvider function, used in the demo above, creates the default completion function that the ExpressionEditor uses. Depending on your use case, you may want to create a custom provider. A completion provider follows this interface: The cursorPosition parameter represents the current token index. Use this value to determine which token the cursor is over and return appropriate completions. The return type may be a promise. The ExpressionEditor supports asynchronous completion loading, which is useful for large completion sets. However, synchronous providers typically provide a better user experience.","link":"docs/expressions-overview#completion-provider","depth":3}]