├── .github ├── card.png └── demo.gif ├── .gitignore ├── LICENSE.md ├── README.md ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── src ├── index.tsx └── utils.tsx ├── test └── benchmark │ ├── gen.js │ ├── ssr-balancer.html │ └── ssr.html ├── tsconfig.json ├── tsup.config.ts ├── turbo.json └── website ├── app ├── layout.tsx ├── page.tsx └── style.css ├── css.d.ts ├── next-env.d.ts ├── next.config.js ├── package.json ├── public ├── android-icon-144x144.png ├── android-icon-192x192.png ├── android-icon-36x36.png ├── android-icon-48x48.png ├── android-icon-72x72.png ├── android-icon-96x96.png ├── apple-icon-114x114.png ├── apple-icon-120x120.png ├── apple-icon-144x144.png ├── apple-icon-152x152.png ├── apple-icon-180x180.png ├── apple-icon-57x57.png ├── apple-icon-60x60.png ├── apple-icon-72x72.png ├── apple-icon-76x76.png ├── apple-icon-precomposed.png ├── apple-icon.png ├── bench.svg ├── dark-android-icon-144x144.png ├── dark-android-icon-192x192.png ├── dark-android-icon-36x36.png ├── dark-android-icon-48x48.png ├── dark-android-icon-72x72.png ├── dark-android-icon-96x96.png ├── dark-apple-icon-114x114.png ├── dark-apple-icon-120x120.png ├── dark-apple-icon-144x144.png ├── dark-apple-icon-152x152.png ├── dark-apple-icon-180x180.png ├── dark-apple-icon-57x57.png ├── dark-apple-icon-60x60.png ├── dark-apple-icon-72x72.png ├── dark-apple-icon-76x76.png ├── dark-apple-icon-precomposed.png ├── dark-apple-icon.png ├── dark-favicon-16x16.png ├── dark-favicon-32x32.png ├── dark-favicon-96x96.png ├── dark-favicon.ico ├── dark-ms-icon-144x144.png ├── dark-ms-icon-150x150.png ├── dark-ms-icon-310x310.png ├── dark-ms-icon-70x70.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-96x96.png ├── favicon.ico ├── ms-icon-144x144.png ├── ms-icon-150x150.png ├── ms-icon-310x310.png ├── ms-icon-70x70.png └── og.png ├── src ├── components │ ├── BlankLink.tsx │ ├── Comparison.tsx │ ├── Content.tsx │ └── index.ts ├── icons │ ├── Copy.tsx │ ├── GitHub.tsx │ ├── TooltipArrow.tsx │ ├── TooltipTrigger.tsx │ ├── Vercel.tsx │ └── index.ts └── sections │ ├── About.tsx │ ├── CustomBalanceRatio.tsx │ ├── Footer.tsx │ ├── GettingStarted.tsx │ ├── Header.tsx │ ├── Hero.tsx │ ├── HowItWorks.tsx │ ├── Performance.tsx │ ├── UseCases.tsx │ └── index.ts └── tsconfig.json /.github/card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/.github/card.png -------------------------------------------------------------------------------- /.github/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shuding/react-wrap-balancer/704dabd9f9bbdfdb75e4a9b0ecec9b5fa91e559c/.github/demo.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .DS_Store 4 | .next 5 | .turbo 6 | .vscode -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Shu Ding 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://react-wrap-balancer.vercel.app) 2 | 3 | ## Introduction 4 | 5 | [**React Wrap Balancer**](https://react-wrap-balancer.vercel.app) is a simple React Component that makes your titles more readable in different viewport sizes. It improves the wrapping to avoid situations like single word in the last line, makes the content more “balanced”: 6 | 7 |  8 | 9 | ## Usage 10 | 11 | To start using the library, install it to your project: 12 | 13 | ```bash 14 | npm i react-wrap-balancer 15 | ``` 16 | 17 | And wrap text content with it: 18 | 19 | ```jsx 20 | import Balancer from 'react-wrap-balancer' 21 | 22 | // ... 23 | 24 | function Title() { 25 | return ( 26 |
or
11 |
21 |
44 |
27 |
{``}
54 |
70 |
88 | Note that if the native CSS balance (
89 |
68 |
71 |
72 |
77 | npm install react-wrap-balancer
78 |
79 |
82 |
84 |
85 |
86 | The simplest way is to wrap the text content with{' '}
87 | {`
90 | {highlightedCode`import ${'Balancer'} from ${"'react-wrap-balancer'"}\n\n// ...\n\n
92 |
93 | If you have multiple {`\n ${'
`}
91 |
100 | {highlightedCode`import { ${'Provider'} } from ${"'react-wrap-balancer'"}\n\n// ...\n\n${'
102 |
136 | 137 | This library requires React ≥ 16.8.0, and IE 11 is not supported. 138 |
139 |
140 |
12 |
11 |
14 | React Wrap Balancer reduces the width of the content wrapper as much as 15 | it could, before causing an extra line break. When reaching the minimum 16 | width, each line will approximately have the same width, and look more 17 | compact and balanced. 18 |
19 |
20 | Check out the{' '}
21 |
11 |
19 | It is worth to mention that this project is a workaround for the lack of 20 | native support for balanced text wrapping in CSS. It is not perfect as 21 | it adds some performance overhead. However, the performance impact is 22 | usually very trivial and can be ignored in most cases. 23 |
24 |
30 | The following benchmark (
31 |
50 | It shows that when there are less than 100 elements with React Wrap 51 | Balancer in the initial HTML, the per-element impact to the page load 52 | time is less than 0.25 ms. When there are 1,000 elements, that number 53 | increases to ~1 ms. When there are 5,000 elements, the per-element 54 | script execution time becomes ~7 ms. 55 |
56 |62 | These numbers don’t scale linearly because re-layouts usually have 63 | an impact to other elements on the page. Hence the best practice is to 64 | only use this library for title elements when necessary, or use it for 65 | content that is behind user interactions (e.g. tooltips), to avoid 66 | negative impacts to the page performance. 67 |
68 | > 69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /website/src/sections/UseCases.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import { animated } from '@react-spring/web' 4 | import Balancer from 'react-wrap-balancer' 5 | 6 | import Comparison from '../components/Comparison' 7 | import Content from '../components/Content' 8 | import TooltipArrowIcon from '../icons/TooltipArrow' 9 | import TooltipTriggerIcon from '../icons/TooltipTrigger' 10 | 11 | export default function UseCases() { 12 | return ( 13 | <> 14 |
15 |
89 |97 | } 98 | /> 99 |90 | You have wakened not out of sleep, but into a prior dream, and 91 | that dream lies within another, and so on, to infinity, which is 92 | the number of grains of sand. The path that you are to take is 93 | endless, and you will die before you have truly awakened. 94 | 95 |
- Jorge Luis Borges 96 |