diff vs diff3 vs diff2html vs react-diff-view vs diff2html-cli
JavaScript Diff Libraries Comparison
1 Year
diffdiff3diff2htmlreact-diff-viewdiff2html-cliSimilar Packages:
What's JavaScript Diff Libraries?

These libraries are designed to compare and visualize differences between text files or code snippets. They provide functionalities to identify changes, generate diffs, and present them in a user-friendly manner. This is particularly useful in version control systems, code review processes, and collaborative coding environments, where understanding changes is crucial for maintaining code quality and collaboration efficiency.

Package Weekly Downloads Trend
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
diff48,577,1548,368621 kB206 months agoBSD-3-Clause
diff3477,7071011.9 kB2-MIT
diff2html228,3883,0341.99 MB182 months agoMIT
react-diff-view46,7948911.3 MB193 months agoMIT
diff2html-cli23,72655058.9 kB11a year agoMIT
Feature Comparison: diff vs diff3 vs diff2html vs react-diff-view vs diff2html-cli

Diff Algorithm

  • diff:

    The 'diff' package implements various algorithms for computing differences, including Myers' algorithm, which is efficient for comparing large texts. It provides flexibility in choosing the algorithm based on your performance needs.

  • diff3:

    The 'diff3' package specializes in three-way diffing, allowing you to compare changes from two sources against a common ancestor. This is crucial for merging and resolving conflicts in version control systems.

  • diff2html:

    'diff2html' does not implement diff algorithms itself but relies on the output from other diff libraries. It focuses on formatting and presenting the diff results in HTML, making it easy to read and understand.

  • react-diff-view:

    'react-diff-view' uses the 'diff' library under the hood to compute differences and provides a React component for rendering those diffs. It is optimized for use in React applications, offering a seamless integration.

  • diff2html-cli:

    Similar to 'diff2html', this CLI tool formats diff outputs into HTML. It does not compute diffs but rather converts existing diff outputs into a more user-friendly format.

Output Format

  • diff:

    The 'diff' library provides output in various formats, including unified and context diffs, which can be customized based on user needs. This flexibility allows for integration into different workflows and tools.

  • diff3:

    The output of 'diff3' is designed to show three-way comparisons, typically in a format that highlights changes from two branches against a base version. This is essential for understanding how changes interact in a merge context.

  • diff2html:

    'diff2html' specifically outputs HTML, making it suitable for web applications that require a visual representation of diffs. It emphasizes readability and user experience in its output.

  • react-diff-view:

    'react-diff-view' renders diffs as React components, allowing for a highly customizable and interactive experience. It can be styled and modified to fit the design of your application.

  • diff2html-cli:

    This CLI tool outputs HTML files from diff outputs, making it easy to generate reports that can be viewed in any web browser. It is particularly useful for generating documentation or reports from diffs.

Integration and Usability

  • diff:

    The 'diff' library is a low-level tool that requires additional implementation for output formatting and presentation. It is best suited for developers who want to build custom solutions around diffing.

  • diff3:

    'diff3' is straightforward for developers familiar with three-way merging concepts. It provides clear outputs for merging scenarios but may require additional handling in user interfaces.

  • diff2html:

    'diff2html' is easy to integrate into web applications, providing a straightforward API for converting diff outputs into HTML. It is user-friendly and requires minimal setup.

  • react-diff-view:

    'react-diff-view' is designed for React applications, making it easy to integrate into existing projects. It provides a component-based approach that fits naturally within the React ecosystem.

  • diff2html-cli:

    As a command-line tool, 'diff2html-cli' is simple to use for generating HTML reports from the terminal. It is ideal for developers who prefer command-line interfaces and need quick results without coding.

Customization

  • diff:

    The 'diff' library allows for extensive customization of the diff algorithms and output formats, making it suitable for developers who need fine control over how differences are computed and displayed.

  • diff3:

    'diff3' allows for some customization in how three-way diffs are displayed, but it is primarily focused on functionality rather than presentation. Users may need to implement their own UI for better visualization.

  • diff2html:

    'diff2html' offers limited customization options focused on HTML output. It is designed for ease of use rather than extensive customization, making it ideal for quick implementations.

  • react-diff-view:

    'react-diff-view' provides a high degree of customization through props and styling, allowing developers to tailor the diff display to their application's needs while leveraging React's component architecture.

  • diff2html-cli:

    Customization in 'diff2html-cli' is minimal, as it primarily serves to convert existing diff outputs. It is best for users who need a quick and straightforward solution without extensive configuration.

Learning Curve

  • diff:

    The 'diff' library has a moderate learning curve, especially for developers unfamiliar with diff algorithms. However, its flexibility can be advantageous for those willing to invest time in understanding its capabilities.

  • diff3:

    The learning curve for 'diff3' is moderate, as understanding three-way diffs requires some familiarity with version control concepts. However, it is straightforward for those experienced in merging strategies.

  • diff2html:

    'diff2html' is easy to learn and implement, making it accessible for developers who want to quickly display diffs in web applications without deep knowledge of diff algorithms.

  • react-diff-view:

    'react-diff-view' is easy to learn for developers already familiar with React. Its component-based structure makes it intuitive to use within React applications.

  • diff2html-cli:

    The CLI tool is straightforward to use, requiring minimal learning for developers familiar with command-line interfaces. It is ideal for quick tasks without needing to delve into code.

How to Choose: diff vs diff3 vs diff2html vs react-diff-view vs diff2html-cli
  • diff:

    Choose 'diff' if you need a lightweight and flexible library for generating diffs between strings or arrays. It is a low-level library that offers various diff algorithms and is suitable for custom implementations where you want to control the output format and processing.

  • diff3:

    Use 'diff3' if you need to handle three-way diffs, which are useful for merging changes from multiple sources. This package is particularly helpful in scenarios where you want to visualize how changes from two branches relate to a common ancestor.

  • diff2html:

    Select 'diff2html' if you want to convert diff outputs into HTML format for easy viewing in web applications. It is ideal for projects that require a simple way to display diffs in a readable format without extensive customization.

  • react-diff-view:

    Choose 'react-diff-view' if you are working within a React application and need a component to display diffs. This library provides a React-based interface for visualizing differences, making it easy to integrate into existing React projects.

  • diff2html-cli:

    Opt for 'diff2html-cli' if you prefer a command-line tool to convert diff outputs into HTML. This is useful for developers who want to quickly generate HTML reports from diffs in their terminal or CI/CD pipelines without integrating it into their codebase.

README for diff

jsdiff

A JavaScript text differencing implementation. Try it out in the online demo.

Based on the algorithm proposed in "An O(ND) Difference Algorithm and its Variations" (Myers, 1986).

Installation

npm install diff --save

Usage

Broadly, jsdiff's diff functions all take an old text and a new text and perform three steps:

  1. Split both texts into arrays of "tokens". What constitutes a token varies; in diffChars, each character is a token, while in diffLines, each line is a token.

  2. Find the smallest set of single-token insertions and deletions needed to transform the first array of tokens into the second.

    This step depends upon having some notion of a token from the old array being "equal" to one from the new array, and this notion of equality affects the results. Usually two tokens are equal if === considers them equal, but some of the diff functions use an alternative notion of equality or have options to configure it. For instance, by default diffChars("Foo", "FOOD") will require two deletions (o, o) and three insertions (O, O, D), but diffChars("Foo", "FOOD", {ignoreCase: true}) will require just one insertion (of a D), since ignoreCase causes o and O to be considered equal.

  3. Return an array representing the transformation computed in the previous step as a series of change objects. The array is ordered from the start of the input to the end, and each change object represents inserting one or more tokens, deleting one or more tokens, or keeping one or more tokens.

API

  • Diff.diffChars(oldStr, newStr[, options]) - diffs two blocks of text, treating each character as a token.

    ("Characters" here means Unicode code points - the elements you get when you loop over a string with a for ... of ... loop.)

    Returns a list of change objects.

    Options

    • ignoreCase: If true, the uppercase and lowercase forms of a character are considered equal. Defaults to false.
  • Diff.diffWords(oldStr, newStr[, options]) - diffs two blocks of text, treating each word and each punctuation mark as a token. Whitespace is ignored when computing the diff (but preserved as far as possible in the final change objects).

    Returns a list of change objects.

    Options

    • ignoreCase: Same as in diffChars. Defaults to false.

    • intlSegmenter: An optional Intl.Segmenter object (which must have a granularity of 'word') for diffWords to use to split the text into words.

      By default, diffWords does not use an Intl.Segmenter, just some regexes for splitting text into words. This will tend to give worse results than Intl.Segmenter would, but ensures the results are consistent across environments; Intl.Segmenter behaviour is only loosely specced and the implementations in browsers could in principle change dramatically in future. If you want to use diffWords with an Intl.Segmenter but ensure it behaves the same whatever environment you run it in, use an Intl.Segmenter polyfill instead of the JavaScript engine's native Intl.Segmenter implementation.

      Using an Intl.Segmenter should allow better word-level diffing of non-English text than the default behaviour. For instance, Intl.Segmenters can generally identify via built-in dictionaries which sequences of adjacent Chinese characters form words, allowing word-level diffing of Chinese. By specifying a language when instantiating the segmenter (e.g. new Intl.Segmenter('sv', {granularity: 'word'})) you can also support language-specific rules, like treating Swedish's colon separated contractions (like k:a for kyrka) as single words; by default this would be seen as two words separated by a colon.

  • Diff.diffWordsWithSpace(oldStr, newStr[, options]) - diffs two blocks of text, treating each word, punctuation mark, newline, or run of (non-newline) whitespace as a token.

  • Diff.diffLines(oldStr, newStr[, options]) - diffs two blocks of text, treating each line as a token.

    Options

    • ignoreWhitespace: true to ignore leading and trailing whitespace characters when checking if two lines are equal. Defaults to false.
    • ignoreNewlineAtEof: true to ignore a missing newline character at the end of the last line when comparing it to other lines. (By default, the line 'b\n' in text 'a\nb\nc' is not considered equal to the line 'b' in text 'a\nb'; this option makes them be considered equal.) Ignored if ignoreWhitespace or newlineIsToken are also true.
    • stripTrailingCr: true to remove all trailing CR (\r) characters before performing the diff. Defaults to false. This helps to get a useful diff when diffing UNIX text files against Windows text files.
    • newlineIsToken: true to treat the newline character at the end of each line as its own token. This allows for changes to the newline structure to occur independently of the line content and to be treated as such. In general this is the more human friendly form of diffLines; the default behavior with this option turned off is better suited for patches and other computer friendly output. Defaults to false.

    Note that while using ignoreWhitespace in combination with newlineIsToken is not an error, results may not be as expected. With ignoreWhitespace: true and newlineIsToken: false, changing a completely empty line to contain some spaces is treated as a non-change, but with ignoreWhitespace: true and newlineIsToken: true, it is treated as an insertion. This is because the content of a completely blank line is not a token at all in newlineIsToken mode.

    Returns a list of change objects.

  • Diff.diffSentences(oldStr, newStr[, options]) - diffs two blocks of text, treating each sentence as a token. The characters ., !, and ?, when followed by whitespace, are treated as marking the end of a sentence; nothing else is considered to mark a sentence end.

    (For more sophisticated detection of sentence breaks, including support for non-English punctuation, consider instead tokenizing with an Intl.Segmenter with granularity: 'sentence' and passing the result to Diff.diffArrays.)

    Returns a list of change objects.

  • Diff.diffCss(oldStr, newStr[, options]) - diffs two blocks of text, comparing CSS tokens.

    Returns a list of change objects.

  • Diff.diffJson(oldObj, newObj[, options]) - diffs two JSON-serializable objects by first serializing them to prettily-formatted JSON and then treating each line of the JSON as a token. Object properties are ordered alphabetically in the serialized JSON, so the order of properties in the objects being compared doesn't affect the result.

    Returns a list of change objects.

    Options

    • stringifyReplacer: A custom replacer function. Operates similarly to the replacer parameter to JSON.stringify(), but must be a function.
    • undefinedReplacement: A value to replace undefined with. Ignored if a stringifyReplacer is provided.
  • Diff.diffArrays(oldArr, newArr[, options]) - diffs two arrays of tokens, comparing each item for strict equality (===).

    Options

    • comparator: function(left, right) for custom equality checks

    Returns a list of change objects.

  • Diff.createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr[, oldHeader[, newHeader[, options]]]) - creates a unified diff patch by first computing a diff with diffLines and then serializing it to unified diff format.

    Parameters:

    • oldFileName : String to be output in the filename section of the patch for the removals
    • newFileName : String to be output in the filename section of the patch for the additions
    • oldStr : Original string value
    • newStr : New string value
    • oldHeader : Optional additional information to include in the old file header. Default: undefined.
    • newHeader : Optional additional information to include in the new file header. Default: undefined.
    • options : An object with options.
      • context describes how many lines of context should be included. You can set this to Number.MAX_SAFE_INTEGER or Infinity to include the entire file content in one hunk.
      • ignoreWhitespace: Same as in diffLines. Defaults to false.
      • stripTrailingCr: Same as in diffLines. Defaults to false.
  • Diff.createPatch(fileName, oldStr, newStr[, oldHeader[, newHeader[, options]]]) - creates a unified diff patch.

    Just like Diff.createTwoFilesPatch, but with oldFileName being equal to newFileName.

  • Diff.formatPatch(patch) - creates a unified diff patch.

    patch may be either a single structured patch object (as returned by structuredPatch) or an array of them (as returned by parsePatch).

  • Diff.structuredPatch(oldFileName, newFileName, oldStr, newStr[, oldHeader[, newHeader[, options]]]) - returns an object with an array of hunk objects.

    This method is similar to createTwoFilesPatch, but returns a data structure suitable for further processing. Parameters are the same as createTwoFilesPatch. The data structure returned may look like this:

    {
      oldFileName: 'oldfile', newFileName: 'newfile',
      oldHeader: 'header1', newHeader: 'header2',
      hunks: [{
        oldStart: 1, oldLines: 3, newStart: 1, newLines: 3,
        lines: [' line2', ' line3', '-line4', '+line5', '\\ No newline at end of file'],
      }]
    }
    
  • Diff.applyPatch(source, patch[, options]) - attempts to apply a unified diff patch.

    Hunks are applied first to last. applyPatch first tries to apply the first hunk at the line number specified in the hunk header, and with all context lines matching exactly. If that fails, it tries scanning backwards and forwards, one line at a time, to find a place to apply the hunk where the context lines match exactly. If that still fails, and fuzzFactor is greater than zero, it increments the maximum number of mismatches (missing, extra, or changed context lines) that there can be between the hunk context and a region where we are trying to apply the patch such that the hunk will still be considered to match. Regardless of fuzzFactor, lines to be deleted in the hunk must be present for a hunk to match, and the context lines immediately before and after an insertion must match exactly.

    Once a hunk is successfully fitted, the process begins again with the next hunk. Regardless of fuzzFactor, later hunks must be applied later in the file than earlier hunks.

    If a hunk cannot be successfully fitted anywhere with fewer than fuzzFactor mismatches, applyPatch fails and returns false.

    If a hunk is successfully fitted but not at the line number specified by the hunk header, all subsequent hunks have their target line number adjusted accordingly. (e.g. if the first hunk is applied 10 lines below where the hunk header said it should fit, applyPatch will start looking for somewhere to apply the second hunk 10 lines below where its hunk header says it goes.)

    If the patch was applied successfully, returns a string containing the patched text. If the patch could not be applied (because some hunks in the patch couldn't be fitted to the text in source), applyPatch returns false.

    patch may be a string diff or the output from the parsePatch or structuredPatch methods.

    The optional options object may have the following keys:

    • fuzzFactor: Maximum Levenshtein distance (in lines deleted, added, or subtituted) between the context shown in a patch hunk and the lines found in the file. Defaults to 0.
    • autoConvertLineEndings: If true, and if the file to be patched consistently uses different line endings to the patch (i.e. either the file always uses Unix line endings while the patch uses Windows ones, or vice versa), then applyPatch will behave as if the line endings in the patch were the same as those in the source file. (If false, the patch will usually fail to apply in such circumstances since lines deleted in the patch won't be considered to match those in the source file.) Defaults to true.
    • compareLine(lineNumber, line, operation, patchContent): Callback used to compare to given lines to determine if they should be considered equal when patching. Defaults to strict equality but may be overridden to provide fuzzier comparison. Should return false if the lines should be rejected.
  • Diff.applyPatches(patch, options) - applies one or more patches.

    patch may be either an array of structured patch objects, or a string representing a patch in unified diff format (which may patch one or more files).

    This method will iterate over the contents of the patch and apply to data provided through callbacks. The general flow for each patch index is:

    • options.loadFile(index, callback) is called. The caller should then load the contents of the file and then pass that to the callback(err, data) callback. Passing an err will terminate further patch execution.
    • options.patched(index, content, callback) is called once the patch has been applied. content will be the return value from applyPatch. When it's ready, the caller should call callback(err) callback. Passing an err will terminate further patch execution.

    Once all patches have been applied or an error occurs, the options.complete(err) callback is made.

  • Diff.parsePatch(diffStr) - Parses a patch into structured data

    Return a JSON object representation of the a patch, suitable for use with the applyPatch method. This parses to the same structure returned by Diff.structuredPatch.

  • Diff.reversePatch(patch) - Returns a new structured patch which when applied will undo the original patch.

    patch may be either a single structured patch object (as returned by structuredPatch) or an array of them (as returned by parsePatch).

  • Diff.convertChangesToXML(changes) - converts a list of change objects to a serialized XML format

  • Diff.convertChangesToDMP(changes) - converts a list of change objects to the format returned by Google's diff-match-patch library

Universal options

Certain options can be provided in the options object of any method that calculates a diff (including diffChars, diffLines etc. as well as structuredPatch, createPatch, and createTwoFilesPatch):

  • callback: if provided, the diff will be computed in async mode to avoid blocking the event loop while the diff is calculated. The value of the callback option should be a function and will be passed the computed diff or patch as its first argument.

    (Note that if the ONLY option you want to provide is a callback, you can pass the callback function directly as the options parameter instead of passing an object with a callback property.)

  • maxEditLength: a number specifying the maximum edit distance to consider between the old and new texts. You can use this to limit the computational cost of diffing large, very different texts by giving up early if the cost will be huge. This option can be passed either to diffing functions (diffLines, diffChars, etc) or to patch-creation function (structuredPatch, createPatch, etc), all of which will indicate that the max edit length was reached by returning undefined instead of whatever they'd normally return.

  • timeout: a number of milliseconds after which the diffing algorithm will abort and return undefined. Supported by the same functions as maxEditLength.

  • oneChangePerToken: if true, the array of change objects returned will contain one change object per token (e.g. one per line if calling diffLines), instead of runs of consecutive tokens that are all added / all removed / all conserved being combined into a single change object.

Defining custom diffing behaviors

If you need behavior a little different to what any of the text diffing functions above offer, you can roll your own by customizing both the tokenization behavior used and the notion of equality used to determine if two tokens are equal.

The simplest way to customize tokenization behavior is to simply tokenize the texts you want to diff yourself, with your own code, then pass the arrays of tokens to diffArrays. For instance, if you wanted a semantically-aware diff of some code, you could try tokenizing it using a parser specific to the programming language the code is in, then passing the arrays of tokens to diffArrays.

To customize the notion of token equality used, use the comparator option to diffArrays.

For even more customisation of the diffing behavior, you can create a new Diff.Diff() object, overwrite its castInput, tokenize, removeEmpty, equals, and join properties with your own functions, then call its diff(oldString, newString[, options]) method. The methods you can overwrite are used as follows:

  • castInput(value, options): used to transform the oldString and newString before any other steps in the diffing algorithm happen. For instance, diffJson uses castInput to serialize the objects being diffed to JSON. Defaults to a no-op.
  • tokenize(value, options): used to convert each of oldString and newString (after they've gone through castInput) to an array of tokens. Defaults to returning value.split('') (returning an array of individual characters).
  • removeEmpty(array): called on the arrays of tokens returned by tokenize and can be used to modify them. Defaults to stripping out falsey tokens, such as empty strings. diffArrays overrides this to simply return the array, which means that falsey values like empty strings can be handled like any other token by diffArrays.
  • equals(left, right, options): called to determine if two tokens (one from the old string, one from the new string) should be considered equal. Defaults to comparing them with ===.
  • join(tokens): gets called with an array of consecutive tokens that have either all been added, all been removed, or are all common. Needs to join them into a single value that can be used as the value property of the change object for these tokens. Defaults to simply returning tokens.join('').
  • postProcess(changeObjects): gets called at the end of the algorithm with the change objects produced, and can do final cleanups on them. Defaults to simply returning changeObjects unchanged.

Change Objects

Many of the methods above return change objects. These objects consist of the following fields:

  • value: The concatenated content of all the tokens represented by this change object - i.e. generally the text that is either added, deleted, or common, as a single string. In cases where tokens are considered common but are non-identical (e.g. because an option like ignoreCase or a custom comparator was used), the value from the new string will be provided here.
  • added: true if the value was inserted into the new string, otherwise false
  • removed: true if the value was removed from the old string, otherwise false
  • count: How many tokens (e.g. chars for diffChars, lines for diffLines) the value in the change object consists of

(Change objects where added and removed are both false represent content that is common to the old and new strings.)

Examples

Basic example in Node

require('colors');
const Diff = require('diff');

const one = 'beep boop';
const other = 'beep boob blah';

const diff = Diff.diffChars(one, other);

diff.forEach((part) => {
  // green for additions, red for deletions
  let text = part.added ? part.value.bgGreen :
             part.removed ? part.value.bgRed :
                            part.value;
  process.stderr.write(text);
});

console.log();

Running the above program should yield

Node Example

Basic example in a web page

<pre id="display"></pre>
<script src="diff.js"></script>
<script>
const one = 'beep boop',
    other = 'beep boob blah',
    color = '';
    
let span = null;

const diff = Diff.diffChars(one, other),
    display = document.getElementById('display'),
    fragment = document.createDocumentFragment();

diff.forEach((part) => {
  // green for additions, red for deletions
  // grey for common parts
  const color = part.added ? 'green' :
    part.removed ? 'red' : 'grey';
  span = document.createElement('span');
  span.style.color = color;
  span.appendChild(document
    .createTextNode(part.value));
  fragment.appendChild(span);
});

display.appendChild(fragment);
</script>

Open the above .html file in a browser and you should see

Node Example

Example of generating a patch from Node

The code below is roughly equivalent to the Unix command diff -u file1.txt file2.txt > mydiff.patch:

const Diff = require('diff');
const file1Contents = fs.readFileSync("file1.txt").toString();
const file2Contents = fs.readFileSync("file2.txt").toString();
const patch = Diff.createTwoFilesPatch("file1.txt", "file2.txt", file1Contents, file2Contents);
fs.writeFileSync("mydiff.patch", patch);

Examples of parsing and applying a patch from Node

Applying a patch to a specified file

The code below is roughly equivalent to the Unix command patch file1.txt mydiff.patch:

const Diff = require('diff');
const file1Contents = fs.readFileSync("file1.txt").toString();
const patch = fs.readFileSync("mydiff.patch").toString();
const patchedFile = Diff.applyPatch(file1Contents, patch);
fs.writeFileSync("file1.txt", patchedFile);
Applying a multi-file patch to the files specified by the patch file itself

The code below is roughly equivalent to the Unix command patch < mydiff.patch:

const Diff = require('diff');
const patch = fs.readFileSync("mydiff.patch").toString();
Diff.applyPatches(patch, {
    loadFile: (patch, callback) => {
        let fileContents;
        try {
            fileContents = fs.readFileSync(patch.oldFileName).toString();
        } catch (e) {
            callback(`No such file: ${patch.oldFileName}`);
            return;
        }
        callback(undefined, fileContents);
    },
    patched: (patch, patchedContent, callback) => {
        if (patchedContent === false) {
            callback(`Failed to apply patch to ${patch.oldFileName}`)
            return;
        }
        fs.writeFileSync(patch.oldFileName, patchedContent);
        callback();
    },
    complete: (err) => {
        if (err) {
            console.log("Failed with error:", err);
        }
    }
});

Compatibility

jsdiff supports all ES3 environments with some known issues on IE8 and below. Under these browsers some diff algorithms such as word diff and others may fail due to lack of support for capturing groups in the split operation.

License

See LICENSE.

Deviations from the published Myers diff algorithm

jsdiff deviates from the published algorithm in a couple of ways that don't affect results but do affect performance:

  • jsdiff keeps track of the diff for each diagonal using a linked list of change objects for each diagonal, rather than the historical array of furthest-reaching D-paths on each diagonal contemplated on page 8 of Myers's paper.
  • jsdiff skips considering diagonals where the furthest-reaching D-path would go off the edge of the edit graph. This dramatically reduces the time cost (from quadratic to linear) in cases where the new text just appends or truncates content at the end of the old text.