- #6721
a85b1c0Thanks @joshbermanssw! - Add runtime Zod validation for thelocalContentPathTina config field (rejects non-string and empty-string values) in the CLI's content-root resolver. Extract content-root resolution out ofprocessConfiginto a standalone, unit-testableresolveContentRootPathfunction that preserves existing behaviour (falls back torootPathwith a warning when the configured directory is missing).localContentPathis deliberately excluded fromtinaConfigZodso it does not leak into the hashed_schema.jsonand break the server-schema match check for projects that set it. Closes tinacms/tinacloud#3294.
- #6548
cd262b3Thanks @isaaclombardssw! - Add 'displayOnly' field type for display-only form fields
- #6633
4315d73Thanks @JackDevAU! - refactor: make @tinacms/schema-tools the single source of truth
- #6447
60510bbThanks @joshbermanssw! - ✨ Object Schema - Add openFormOnCreate flag. Allowing users to opt-in to automatically going inside an object form when the field is true
- #6450
56d533eThanks @18-th! - * Restricted CORS on dev server to localhost by default for GHSA-8pw3-9m7f-q734.- Added
server.allowedOriginsconfig option for non-localhost environments. - Enabled Vite
server.fs.strictwith computed allow list for GHSA-m48g-4wr2-j2h6 - Bind LevelDB TCP server to 127.0.0.1
- Added
-
#6438
f90d47bThanks @joshbermanssw! - Telemetry - We’ve added anonymous telemetry to help us understand how Tina is used and prioritize improvements in areas where it matters. No personal or sensitive data is collected, and the information cannot be used to identify individual users. Telemetry is used solely to improve performance, stability, and the overall user experience. You can opt-out at any time by disabling telemetry in your Tina config.Read more at https://tina.io/telemetry
- #6392
4eb9252Thanks @Calinator444! - Updated type definition for schema tools to include filename.parse
- #6353
1d4fb88Thanks @wicksipedia! - Collections - added ability to make yaml max line width configurable. This was previously hard coded to 80 but now defaults to being disabled-1. This is configurable though the collection'syamlMaxLineWidthprop.
- #6259
6c2c48aThanks @JackDevAU! - Added fuzzy search options + support. This is now set as the new default. Going forward this package will also be used on the TinaCloud side so we don't duplicate code. Search now uses the closest index instead of being exact.
- #6286
a63401aThanks @joshbermanssw! - ✨ Half-width property for text fields in TinaCMS Editor
- #6233
a125472Thanks @joshbermanssw! - ✨ File History Provider
- #5982
2e1535dThanks @brookjeynes-ssw! - feat: migrate from commonjs to esm
- #6183
ed6025eThanks @Pat-Stuart! - Add in ability to disable folder creation in UI per collection
-
#6187
a76fdb6Thanks @18-th! - Updated yup to v1 to remove transitive lodash dependency -
#6138
a736bafThanks @Pat-Stuart! - Update Image field type definition to include uploadDir
- #6142
f2577b9Thanks @joshbermanssw! - Folder creation validation with regex
-
#6147
38920ceThanks @joshbermanssw! - Fix: ui.max not applying to primitive type list:true -
#5822
4b824beThanks @copilot-swe-agent! - Migrate from react-beautiful-dnd to dnd-kit to fix deprecation error.
- #5909
eaa6ed5Thanks @wicksipedia! - feat: added ability to opt out of latest version update check
- #5744
98a61e2Thanks @Ben0189! - Upgrade Plate editor to v48 beta, integrating latest features and improvements.
- #5750
dbef36fThanks @wicksipedia! - Rich text parsers - adds support for slate serialization in json
- #5679
d689189Thanks @brookjeynes-ssw! - feat: dedupe format types by moving them into schema-tools
- #5602
ab43169Thanks @wicksipedia! - fix naming of TinaCloud
- #5497
602b4d0Thanks @kldavis4! - This reimplements references to more thoroughly find deeply nested references and add a reference index to the collection in order to query these deep references. The rename modal is updated to remove the warning about breaking references as well.
- #5486
d7c5ec1Thanks @JackDevAU! - Update dependencies across packages
- #5388
92b683bThanks @JackDevAU! - ✨ Adds newoverridesprop to rich text fields.toolbarOverrideis now deprecated in favor ofoverrides.toolbar.
- #5363
c45ac5dThanks @brookjeynes-ssw! - Improve schema validation messages
-
#5276
f90ef4dThanks @Ben0189! - Updates minor and patch dependencies -
#5252
ac2003fThanks @JackDevAU! - Addtableto the Schema Tools ToolbarOverrideType list -
#5218
03bb823Thanks @kldavis4! - Adds referential integrity for renaming and deleting referenced documents.When a document is renamed, any documents which reference the document will be updated with the new document name. When a document is deleted, the user will be warned and any references to the document will be deleted.
- #5225
0daf0b6Thanks @JackDevAU! - ⬆️ Addresses peer dependency issues and applies necessary updates
- #4825
ecea7acThanks @JackDevAU! - ✨ Add Mermaid Support to Rich Text Field (Plate) 🐛 Fix tooltip rendering behind TinaCMS app
- #4780
31513bbThanks @Ben0189! - improve reference field selector filter and simplyfy schema needed to be written by user
- #4843
4753c9bThanks @JackDevAU! - ⬆️ Update Minor & Patch Dependencies Versions
-
#4832
6cd3596Thanks @wicksipedia! - Adds config options to allow users to customize position + description of filename fields -
#4804
d08053eThanks @dependabot! - ⬆️ Updates Typescript to v5.5, @types/node to v22.x, next.js to latest version 14.x, and removes node-fetch
- 6ccda6c: ⚗️ Experimental - added reference field filter Note: this will be deprecated in the near future for a more robust soution
- 33eaa81: Minor code refactor in reference field selector
- ae03e8e: Implementation for custom reference field selector
-
324950a: Updates Plate Editor to latest version 36.
- Upgrades all remaining packages
Typescriptto version^5 - Adds Shadcn/ui styles/colours to our
tinatailwindconfig (packages/@tinacms/cli/src/next/vite/tailwind.ts) - Replaces some
lodashdeps with either the specific function i.e.lodash.setor implements them in a utility file - Updates and removes old version of plate (
plate-headless) for latest version^36 - Starts removing and cleaning up some of the old Plate code.
- Upgrades all remaining packages
- cb83dc2: add
toolbarOverrideoption torich-textfields
- f567fc8: More React 18 upgrades and fixes
- e58b951: update vulnerable packages so npm audit does not complain
- 957fa26: update to React 18
- 9076d09: update next js version from 12 to 14 in tinacms packages
- f26b40d: Allow customization of accepted media types in media manager
- 0503072: update ts, remove rimraf, fix types
- dffa355: Remove yarn for pnpm
- 2e3393ef5: Implement Create folder feature for tinacms.
- 64f8fa038: This extends the existing
LoginStrategytype to include a newLoginScreenoption. AgetLoginScreenfunction can be set on the AuthProvider to display a custom login screen, rather than showing the modal popups and forcing a redirect or displaying the default username and password form. This will hopefully simplify the process of creating custom auth providers and handling user authentication when self-hosting.
-
a65ca13f2: ## TinaCMS Self hosted Updates
- Deprecated:
onPut,onDelete, andlevelarguments increateDatabase. - Added:
databaseAdapterto replacelevel. - Added:
gitProviderto substituteonPutandonDelete. - New Package:
tinacms-gitprovider-github, exporting theGitHubProviderclass. - Interface Addition:
gitProvideradded to@tinacms/graphql. - Addition: Generated database client.
import { createDatabase, createLocalDatabase } from "@tinacms/datalayer"; import { MongodbLevel } from "mongodb-level"; import { GitHubProvider } from "tinacms-gitprovider-github"; const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === "true"; export default isLocal ? createLocalDatabase() : createDatabase({ gitProvider: new GitHubProvider({ branch: process.env.GITHUB_BRANCH, owner: process.env.GITHUB_OWNER, repo: process.env.GITHUB_REPO, token: process.env.GITHUB_PERSONAL_ACCESS_TOKEN, }), databaseAdapter: new MongodbLevel<string, Record<string, any>>({ collectionName: "tinacms", dbName: "tinacms", mongoUri: process.env.MONGODB_URI, }), namespace: process.env.GITHUB_BRANCH, });
- GitHubProvider Usage: Replace
onPutandonDeletewithgitProvider, using the providedGitHubProviderfor GitHub.
const gitProvider = new GitHubProvider({ branch: process.env.GITHUB_BRANCH, owner: process.env.GITHUB_OWNER, repo: process.env.GITHUB_REPO, token: process.env.GITHUB_PERSONAL_ACCESS_TOKEN, });
- Custom Git Provider: Implement the
GitProviderinterface for different git providers.
If you are not using Github as your git provider, you can implement the
GitProviderinterface to use your own git provider.class CustomGitProvider implements GitProvider async onPut(key: string, value: string) // ... async onDelete(key: string) // ... const gitProvider = new CustomGitProvider();
- Renaming in Code: Change
leveltodatabaseAdapterfor clarity.
createDatabase({ - level: new MongodbLevel<string, Record<string, any>>(...), + databaseAdapter: new MongodbLevel<string, Record<string, any>>(...), })- Usage: Implement a local database with the
createLocalDatabasefunction.
import { createLocalDatabase } from "@tinacms/datalayer"; createLocalDatabase(port);
- Updated
database.{ts,js}File:
import { createDatabase, createLocalDatabase, GitHubProvider } from '@tinacms/datalayer'; import { MongodbLevel } from 'mongodb-level'; const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true'; export default isLocal ? createLocalDatabase() : createDatabase({ gitProvider: new GitHubProvider(...), databaseAdapter: new MongodbLevel<string, Record<string, any>>(...), });
- New:
authProviderindefineConfig. - Class:
AbstractAuthProviderfor extending new auth providers. - Clerk Auth Provider: New provider added.
- Renaming:
admin.authtoadmin.authHooks. - Deprecation:
admin.auth.
- Transition: From auth functions to
authProviderclass.
- Previous API:
defineConfig({ admin: { auth: { login() {}, logout() {}, //... }, }, //... });
- New API:
import { AbstractAuthProvider } from "tinacms"; class CustomAuthProvider extends AbstractAuthProvider { login() {} logout() {} //... } defineConfig({ authProvider: new CustomAuthProvider(), //... });
-
New: TinaNodeBackend is exported from
@tinacms/datalayer. This is used to host the TinaCMS backend in a single function. -
New:
LocalBackendAuthProvideris exported from@tinacms/datalayer. This is used to host the TinaCMS backend locally. -
New:
AuthJsBackendAuthProvideris exported fromtinacms-authjs. This is used to host the TinaCMS backend with AuthJS.
Now, instead of hosting the in /tina/api/gql.ts file, the entire TinaCMS backend (including auth) will be hosted in a single backend function.
/api/tina/[...routes].{ts,js}import { TinaNodeBackend, LocalBackendAuthProvider, } from "@tinacms/datalayer"; import { TinaAuthJSOptions, AuthJsBackendAuthProvider } from "tinacms-authjs"; import databaseClient from "../../../tina/__generated__/databaseClient"; const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === "true"; const handler = TinaNodeBackend({ authProvider: isLocal ? LocalBackendAuthProvider() : AuthJsBackendAuthProvider({ authOptions: TinaAuthJSOptions({ databaseClient: databaseClient, secret: process.env.NEXTAUTH_SECRET, }), }), databaseClient, }); export default (req, res) => { // Modify the request here if you need to return handler(req, res); };
These changes are put in place to make self hosted TinaCMS easier to use and more flexible.
Please check out the docs for more information on self hosted TinaCMS.
- Deprecated:
- 6861b5e01: Fix bug where match property was not working on windows
- aec44a7dc: Allow backend/client to include partial queries
- 7e4de0b2a: Improvements to error handling with auth
- 099bf5646: Make router optionally async
- c92de7b1d: fix: Fix matches regression where matches on collections wouldn't match proper collection
-
1563ce5b2: Update the router function to work asynchronously. This means that a user can now fetch data or perform other async operations in the router function.
Example:
router: async ({ document }) => { const res = await client.queries.post({ relativePath: document._sys.relativePath, }) return `/posts/${res.data.slug}` },
-
133e97d5b: Update the before submit types to not pass the finalForm form since it is contained in the TinaForm
-
f02b4368b: Adds a second parameter to the slugify function that passes the current collection and template.
-
7991e097e: Add a
beforeSubmithook function on a collection.ui. This give users the ability to run a function before the form is submitted.If the function returns values those values will be used will be submitted instead of the form values.
If the function returns a falsy value the original form values will be submitted.
// tina/config.{ts.js} export default defineConfig({ schema: { collections: [ { ui: { // Example of beforeSubmit beforeSubmit: async ({ values }) => { return { ...values, lastUpdated: new Date().toISOString(), }; }, //... }, //... }, //... ], }, //... });
- bc812441b: Use .mjs extension for ES modules
- 019920a35: Make clientId, branch and token optional in the config.
- fe13b4ed9: Fix search index tokenizer regex to not treat underscores as token separators
- a94e123b6: Fix optional config validation for search
- c385b5615: Initial implementation of search functionality
-
beb179279: Add support for sites deployed to sub-paths. To enabled, provide the sub-path at config.build.basePath:
... build: { outputFolder: 'admin', publicFolder: 'public', basePath: 'my-site', // site is served at my-domain.com/my-site }, ...
- f14f59a96: Handle
path: "/"in a collection - eeedcfd30: Adds folder support in the admin. See this PR for more info and a demo.
- a70204500: feat: Configurable template key on blocks
- 9a8074889: Consolidate payload transform logic
- c48326846: Move --skipSDK into config property:
client.skip = true
- 76c984bcc: Use new API endpoint in content api reqests
- 5809796cf: Adds match property to collection
- 54aac9017: Support more forgiving markdown parser for non-MDX collections. Previously, this feature was only available by opting in
with the
parser: {type: "markdown"}configuration on a rich-text field. Now, allrich-textfields will adhere to the appropriate parser based on their collection format, but can continue to be overridden manually.
- d1cf65999: Adds list support to image field, improved field UI
-
0a5297800: feat: Allow adding aliases in field configs, to export special characters like names with dashes, or fields named "id"
-
7a3e86ba1: fix: templates regression
-
353899de1: Deprecate and reconfigure internal types. Most user-facing types should remain unchanged aside from a couple of bug fixes:
- Fix missing
indexedandindexesproperties oncollectionandfieldconfigs.
Deprecations
CollectionFieldsWithNamespace CollectionTemplates CollectionTemplatesWithNamespace GlobalTemplate ObjectType ReferenceType ReferenceTypeInner ReferenceTypeWithNamespace RichTextType RichTypeWithNamespace TinaCloudCollection TinaCloudCollectionBase TinaCloudCollectionEnriched TinaCloudSchema TinaCloudSchemaBase TinaCloudSchemaConfig TinaCloudSchemaEnriched TinaCloudSchemaWithNamespace TinaCloudTemplateBase TinaCloudTemplateEnriched TinaFieldBase TinaFieldEnriched TinaFieldInner TinaTemplateRemovals
Templatable TinaIndex ResolveFormArgs - Fix missing
-
01b858e41: Better error message on the name field
- 892b4e39e: Fix issue where return type for
ui.validatewas incorrect - c97ffc20d: Add schema checks to ensure the local and server schema are the same.
- e732906b6: Support .yml file extension for yaml files
- efd56e769: Replace Store with AbstractLevel in Database. Update CLI to allow user to configure Database.
- efd56e769: Remove license headers
-
84fe97ca7: Fix issue where deeply nested template objects inside field objects weren't transformed on save properly
-
e7c404bcf: Support remote path configuration for separate content repos
Tina now supports serving content from a separate Git repo.
To enable this during local development, point this config at the root of the content repo.
NOTE: Relative paths are fine to use here, but make sure it's relative to the
.tina/configfilelocalContentPath: process.env.REMOTE_ROOT_PATH; // eg. '../../my-content-repo'
For production, your config should use the
clientId,branch, andtokenvalues that are associated with your content repo.
- 3165f397d: fix: Shortcodes need to be specified by name to match with match-start / match-end
- 7d41435df: added ability to use toml in markdown frontmatter
- b2952a298: Adds meta wrapper for list-type fields that displays errors. Adds optional min/max for list-type fields that controls add/remove UI. Removes duplicate label from group field.
- 7554ea362: Adds hidden and button toggle fields, Improves toggle, radio, checkbox, select, textarea, and list field styles, Fixes block field UI, adds ability to set label to false on any field.
- 4ebc44068: Add a migration tool for forestry users
- 7495f032b: Added
onLogouthook function and a logout redirect page in the admin - de37c9eff: Content is now merged with existing content. This means if you have a field that is not defined in the schema it will not be overridden.
-
c91bc0fc9: Tweak CLI styling for create-tina-app, tinacms dev, and tinacms init
-
c1ac4bf10: Added a
onLoginCallback function that is called when the user logs in.EX:
import { defineConfig } from "tinacms"; export default defineConfig({ admin: { auth: { onLogin: () => { console.log("On Log in!"); }, }, }, /// ... });
- 08e02ec21: Add types for allowedActions in the config
-
958d10c82: Tina 1.0 Release
Make sure you have updated to th "iframe" path: https://tina.io/blog/upgrading-to-iframe/
-
a5d6722c7: Adds the ability to hide the delete and create buttons.
EX,
export default defineConfig({ collections: [ { label: "Global", name: "global", path: "content/global", ui: { global: true, allowedActions: { create: false, delete: false, }, }, format: "json", fields: [ //... ], }, ], });
- 6c93834a2: Update config and schema types
- 774abcf9c: -
staticConfigbecomesdefineConfigdefineConfigbecomesdefineLegacyConfig- Deprecate
configproperty in the schema
- 245a65dfe: Fix issue saving deeply nested objects in visual mode
- c4f9607ce: Add validation to schema
- 005e1d699: update itemProps types
- b1a357f60: Update object field types to include
defaultItem
- c6e3bd321: Fix issue where slugify function breaks templates
-
183249b11: - deprecate:
defaultValue- add
defaultItemto the collection (as a function or an object)
defaultItem: () => { const m = new Date() return { title: 'New Page', test: 'This is a default value of the test field', filename: `new-page-${ m.getUTCFullYear() + '-' + (m.getUTCMonth() + 1) + '-' + m.getUTCDate() }`, } },
- Allow
datetimefield to be undefined or empty
- add
-
8060d0949: Provide filename customization API.
name: 'posts', path: 'content/posts', ui: { filename: { slugify: (values) => mySlugifyFunc(values), disabled: true // other field props like `label`, `component`, `parse` can still be used too } },
If one is using
isTitlea default slugify function is added that slugifys the title.
- f581f263d: Add --static option for
tina init - 7ae1b0697: Remove duplicate TinaSchema class
- f3439ea35: Replace loading message and hide forms while loading.
- 48032e2ba: Use tinaio url config override in the client
-
9183157c4: This allows us to use a leaner
definefunction for the standalone config. Right now we're balancing a lot on thedefineSchema/defineConfigtypes and have a few overlapping things likeclient, which accepts both an optional object withreferenceDepthconfig as well as the autogenerated http client.One thing it does that's a bit different is it uses the
apiUrlfrom the client generation function and sends it through as a global constant to the Vite app, this avoids the need for the generatedclient. -
4adf12619: Add support for experimental iframe mode
-
f8b89379c: Fixed an issue with windows paths not working.
- 777b1e08a: add better error messages for duplicate values in zod
-
59ff1bb10: fix: fix collection fetching when paths overlap
-
232ae6d52: Added better checks for name field in schema
-
fd4d8c8ff: Add
routerproperty on collections. This replaces the need for using the RouteMapper plugin.... name: 'post', path: 'posts', ui: { router: ({ document }) => { // eg. post items can be previewed at posts/hello-world return `/posts/${document._sys.filename}`; }, }, ...
Add
globalproperty on collections. This replaces the need forformifyCallbackin most cases... name: 'post', path: 'posts', ui: { global: true }, ...
-
9e5da3103: Add router to default schema
-
7b0dda55e: Updates to the
rich-textcomponent as well the shape of therich-textfield response from the API- Adds support for isTitle on MDX elements
- Fixes issues related to nested marks
- Uses monaco editor for code blocks
- Improves styling of nested list items
- Improves handling of rich-text during reset
- No longer errors on unrecognized JSX/html, instead falls back to print
No component provided for <compnonent name> - No longer errors on markdown parsing errors, instead falls back to rendering markdown as a string, customizable via the TinaMarkdown component (invalid_markdown prop)
- Prepares rich-text component for raw mode - where you can edit the raw markdown directly in the Tina form. This will be available in future release.
-
8183b638c: ## Adds a new "Static" build option.
This new option will build tina into a static
index.htmlfile. This will allow someone to use tina without having react as a dependency.- Add a
.tina/config.{js,ts,tsx,jsx}with the default export of define config.
// .tina/config.ts import schema from "./schema"; export default defineConfig({ schema: schema, //.. Everything from define config in `schema.ts` //.. Everything from `schema.config` });
- Add Build config
.tina/config.ts export default defineConfig({ build: { outputFolder: "admin", publicFolder: "public", }, //... other config })- Go to
http://localhost:3000/admin/index.htmland view the admin
- Add a
-
870a32f18: This PR adds the new generated client, a new build command and introduces a new path of working with tina.
Instead of passing an ApiURL, now the clientId, branch and read only token (NEW) will all be configured in the schema. The local url will be used if the --local flag is passed.
This will require a change to the schema and the scripts.
// .tina/schema.ts + import { client } from "./__generated__/client"; // ... const schema = defineSchema({ + config: { + branch: "main", + clientId: "***", + token: "***", }, collections: [ // ... ] }) // ... - const branch = process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF - const clientId = 'YOUR-CLIENT-ID-HERE' - const apiURL = - process.env.NODE_ENV == 'development' - ? 'http://localhost:4001/graphql' - : `https://content.tinajs.io/content/${clientId}/github/${branch}` export const tinaConfig = defineConfig({ + client, - apiURl, schema, // ... }) export default schema
The token must be a wildcard token (
*) and can be generated from the tina dashboard. Read more hearWe now recommend separating the graphQL server into two separate processes (two separate terminals in development). The scripts should look like this:
{ "scripts": { "dev": "tinacms build --local && next dev", "dev-server": "tinacms server:start", "build": "tinacms build && next build" // ... Other Scripts } }When developing, in the first terminal run
yarn dev-serverand thenyarn devin the second.The old
-csubcommand can still be used. This will start the dev server and next dev process in the same terminal.{ "scripts": { "dev": "tinacms server:start \"tinacms build --local && next dev\"", "dev-server": "tinacms server:start", "build": "tinacms build && next build" // ... Other Scripts } }We now recommend ignoring most of the generated files. This is because
client.tsandtypes.tswill be generated in CI withtinacms buildTo remove them from your repository, run
git rm --cached .tina/__generated__/*and thenyarn tinacms buildto update the generated files that need to stay. -
660247b6b: Throw an error message when name contains spaces
- b0dfc6205: Fixed bug where objects where not being copied
- 7d87eb6b7: Add
loadCustomStoreto top schema config - 67e291e56: Add support for ES modules
- ae23e9ad6: Remove unused deps from monorepo
- fb73fb355: Renames syncFolder to a mediaRoot when configuring Repo-Based Media
- f6cb634c2: Added an optional config key to the schema that will be used for TinaCloud media store
- 6e2ed31a2: Added
isTitleproperty to the schema that allows the title to be displayed in the CMS
- 921709a7e: Adds validation to the schema instead of only using typescript types
-
abf25c673: The schema can now to used on the frontend (optional for now but will be the main path moving forward).
If you gone though the
tinacms initprocess there should be a file called.tina/components/TinaProvider. In that file you can import the schema fromschema.tsand add it to the TinaCMS wrapper component.import TinaCMS from "tinacms"; import schema, { tinaConfig } from "../schema.ts"; // Importing the TinaProvider directly into your page will cause Tina to be added to the production bundle. // Instead, import the tina/provider/index default export to have it dynamially imported in edit-moode /** * * @private Do not import this directly, please import the dynamic provider instead */ const TinaProvider = ({ children }) => { return ( <TinaCMS {...tinaConfig} schema={schema}> {children} </TinaCMS> ); }; export default TinaProvider;
-
801f39f62: Update types
-
e8b0de1f7: Add
parentTypenameto fields to allow us to disambiguate between fields which have the same field names but different types. Example, an event from field name ofblocks.0.titlecould belong to aCtablock or aHeroblock, both of which have atitlefield.