├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .gitpod.yml ├── .kktrc.ts ├── README.md ├── package.json ├── public ├── favicon.ico └── index.html ├── renovate.json ├── src ├── app │ ├── App.module.css │ ├── App.test.tsx │ ├── App.tsx │ └── logo.svg ├── index.css ├── index.tsx └── react-app-env.d.ts └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: jaywcjlove 2 | buy_me_a_coffee: jaywcjlove 3 | custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"] 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | build-deploy: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: 20 15 | registry-url: 'https://registry.npmjs.org' 16 | 17 | - run: npm install --legacy-peer-deps 18 | - run: npm run build 19 | - run: npm run coverage 20 | - run: cp -rp coverage/lcov-report build/ 21 | 22 | - name: Create Coverage Badges 23 | uses: jaywcjlove/coverage-badges-cli@main 24 | with: 25 | output: build/badges.svg 26 | 27 | - name: Generate Contributors Images 28 | uses: jaywcjlove/github-action-contributors@main 29 | with: 30 | filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\]) 31 | output: build/CONTRIBUTORS.svg 32 | avatarSize: 42 33 | 34 | - name: Deploy 35 | uses: peaceiris/actions-gh-pages@v4 36 | with: 37 | github_token: ${{ secrets.GITHUB_TOKEN }} 38 | publish_dir: ./build 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | build 3 | node_modules 4 | npm-debug.log* 5 | package-lock.json 6 | 7 | .eslintcache 8 | .DS_Store 9 | .cache 10 | .vscode 11 | 12 | *.bak 13 | *.tem 14 | *.temp 15 | #.swp 16 | *.*~ 17 | ~*.* 18 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | ports: 2 | - port: 3000 3 | onOpen: open-preview 4 | tasks: 5 | - init: npm install 6 | command: npm run start -------------------------------------------------------------------------------- /.kktrc.ts: -------------------------------------------------------------------------------- 1 | import webpack, { Configuration } from 'webpack'; 2 | import lessModules from '@kkt/less-modules'; 3 | import { LoaderConfOptions } from 'kkt'; 4 | import pkg from './package.json'; 5 | 6 | export default (conf: Configuration, env: 'development' | 'production', options: LoaderConfOptions) => { 7 | conf = lessModules(conf, env, options); 8 | // Get the project version. 9 | conf.plugins!.push( 10 | new webpack.DefinePlugin({ 11 | VERSION: JSON.stringify(pkg.version), 12 | }), 13 | ); 14 | if (env === 'production') { 15 | conf.output = { ...conf.output, publicPath: './' }; 16 | conf.optimization = { 17 | ...conf.optimization, 18 | splitChunks: { 19 | cacheGroups: { 20 | reactvendor: { 21 | test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/, 22 | name: 'react-vendor', 23 | chunks: 'all', 24 | }, 25 | refractor: { 26 | test: /[\\/]node_modules[\\/](refractor)[\\/]/, 27 | name: 'refractor-vendor', 28 | chunks: 'all', 29 | }, 30 | codemirror: { 31 | test: /[\\/]node_modules[\\/](@codemirror)[\\/]/, 32 | name: 'codemirror-vendor', 33 | chunks: 'all', 34 | }, 35 | reactjsonview: { 36 | test: /[\\/]node_modules[\\/](react-json-view)[\\/]/, 37 | name: 'react-json-view-vendor', 38 | chunks: 'all', 39 | }, 40 | lezer: { 41 | test: /[\\/]node_modules[\\/](@lezer)[\\/]/, 42 | name: 'lezer-vendor', 43 | chunks: 'all', 44 | }, 45 | }, 46 | }, 47 | }; 48 | } 49 | return conf; 50 | }; 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [`Online JSON Viewer`](https://uiwjs.github.io/json-viewer) 2 | 3 | [](https://github.com/uiwjs/json-viewer/actions/workflows/ci.yml) 4 | [](https://uiwjs.github.io/json-viewer/lcov-report/) 5 | [](https://gitpod.io/#https://github.com/uiwjs/json-viewer) 6 | [](https://uiw.gitee.io/json-viewer) 7 | 8 | Online JSON Viewer, JSON Beautifier to beautify and tree view of JSON data - It works as JSON Pretty Print to pretty print JSON data. 9 | 10 | https://uiwjs.github.io/json-viewer 11 | 12 | 13 | Relate: https://jaywcjlove.github.io/tools/#/json-format 14 | 15 | [](https://uiwjs.github.io/json-viewer) 16 | 17 | **URL Parameters:** 18 | 19 | ```bash 20 | # Hiden Header 21 | https://uiwjs.github.io/json-viewer/#/?hidenheader=1 22 | # Hiden GitHub Corners 23 | https://uiwjs.github.io/json-viewer/#/?corner=1 24 | # GitHub corners href 25 | https://uiwjs.github.io/json-viewer/#/?corner=0&cornerhref=cornerhref=https://github.com/uiwjs/json-viewer 26 | # view=preview|editor 27 | https://uiwjs.github.io/json-viewer/#/?view=preview&json=%7B%0A%20%20"a":%20234%0A%7D 28 | # Share Code 29 | https://uiwjs.github.io/json-viewer/#/?json=%7B%0A%20%20"a":%20234%0A%7D 30 | ``` 31 | 32 | **Reference:** 33 | 34 | https://www.json.org/json-en.html 35 | https://tools.ietf.org/html/rfc8259 (The latest IETF RFC – contains the latest full spec) 36 | 37 | **Contributors** 38 | 39 | As always, thanks to our amazing contributors! 40 | 41 | 42 | 43 | 44 | 45 | Made with [github-action-contributors](https://github.com/jaywcjlove/github-action-contributors). 46 | 47 | **License** 48 | 49 | Licensed under the MIT License. 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-viewer", 3 | "version": "1.0.0", 4 | "description": "Online JSON Viewer, JSON Beautifier to beautify and tree view of JSON data - It works as JSON Pretty Print to pretty print JSON data.", 5 | "private": true, 6 | "scripts": { 7 | "start": "kkt start", 8 | "build": "kkt build", 9 | "test": "kkt test --env=jsdom", 10 | "coverage": "kkt test --env=jsdom --coverage --bail", 11 | "map": "source-map-explorer build/static/js/*.js --html build/website-result.html" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/uiwjs/json-viewer.git" 16 | }, 17 | "author": "", 18 | "license": "MIT", 19 | "dependencies": { 20 | "@uiw/react-copy-to-clipboard": "~4.21.0", 21 | "@uiw/react-github-corners": "~1.5.3", 22 | "@uiw/react-json-view": "^1.0.0", 23 | "@uiw/react-split": "~5.8.7", 24 | "@uiw/react-codemirror": "^4.11.5", 25 | "@codemirror/lang-json": "^6.0.1", 26 | "history": "^5.3.0", 27 | "react": "^18.2.0", 28 | "react-dom": "^18.2.0" 29 | }, 30 | "devDependencies": { 31 | "@kkt/less-modules": "^7.5.1", 32 | "@types/react": "^18.0.17", 33 | "@types/react-dom": "^18.0.6", 34 | "source-map-explorer": "~2.5.2", 35 | "kkt": "^7.5.1" 36 | }, 37 | "jest": { 38 | "coverageReporters": [ 39 | "lcov", 40 | "json-summary" 41 | ], 42 | "testMatch": [ 43 | "/src/**/*.test.{ts,tsx}" 44 | ], 45 | "transformIgnorePatterns": [ 46 | "/node_modules/?!(.*)" 47 | ], 48 | "collectCoverageFrom": [ 49 | "/src/app/*.{tsx,ts}" 50 | ] 51 | }, 52 | "eslintConfig": { 53 | "extends": [ 54 | "react-app", 55 | "react-app/jest" 56 | ] 57 | }, 58 | "browserslist": { 59 | "production": [ 60 | ">0.2%", 61 | "not dead", 62 | "not op_mini all" 63 | ], 64 | "development": [ 65 | "last 1 chrome version", 66 | "last 1 firefox version", 67 | "last 1 safari version" 68 | ] 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uiwjs/json-viewer/6589672ba1b7c05d1864653d410834ddbb868ea1/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Best JSON Viewer and JSON Beautifier Online 12 | 13 | 14 | 15 | 16 | You need to enable JavaScript to run this app. 17 | 18 | 19 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "packageRules": [ 6 | { 7 | "matchPackagePatterns": ["*"], 8 | "rangeStrategy": "replace" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/app/App.module.css: -------------------------------------------------------------------------------- 1 | .app { 2 | height: 100%; 3 | } 4 | 5 | .header { 6 | min-height: 32px; 7 | background: #dcdcdc; 8 | display: flex; 9 | align-items: center; 10 | padding-left: 8px; 11 | font-weight: bold; 12 | } 13 | 14 | .header h1 { 15 | margin: 0; 16 | padding: 0; 17 | font-size: 16px; 18 | } 19 | 20 | .header button { 21 | margin: 0 3px; 22 | } 23 | .header .btn button:first-child { 24 | margin-left: 12px; 25 | } 26 | 27 | .message { 28 | background: #ff000087; 29 | margin-left: 12px; 30 | padding: 2px 4px; 31 | border-radius: 2px; 32 | color: white; 33 | } 34 | 35 | .toolbar { 36 | padding: 0 5px; 37 | padding-left: 12px; 38 | font-size: 12px; 39 | font-weight: normal; 40 | display: flex; 41 | align-items: center; 42 | justify-content: space-between; 43 | } -------------------------------------------------------------------------------- /src/app/App.test.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { createRoot } from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | function AppWithCallbackAfterRender() { 7 | useEffect(() => { 8 | console.log('rendered'); 9 | }); 10 | 11 | return 12 | } 13 | 14 | const div = document.createElement('div'); 15 | const root = createRoot(div!); 16 | root.render(); 17 | root.unmount(); 18 | }); 19 | -------------------------------------------------------------------------------- /src/app/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback, useEffect, useRef} from 'react'; 2 | import Split from '@uiw/react-split'; 3 | import GitHubCorners from '@uiw/react-github-corners'; 4 | import JsonViewer from '@uiw/react-json-view'; 5 | import CodeMirror, { ReactCodeMirrorRef } from '@uiw/react-codemirror'; 6 | import { json as jsonLang } from '@codemirror/lang-json'; 7 | import { createHashHistory } from 'history'; 8 | import styles from './App.module.css'; 9 | 10 | type Parameters = { 11 | json?: string; 12 | cornerhref?: string; 13 | hidenheader?: '1' | '0'; 14 | corner?: '1' | '0'; 15 | view?: 'preview'| 'editor'; 16 | } 17 | const history = createHashHistory(); 18 | const getURLParameters = (url: string): Parameters => 19 | ((url.match(/([^?=&]+)(=([^&]*))/g) || []) as any).reduce( 20 | (a: any, v: string) => ( 21 | ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a) 22 | ), 23 | {} 24 | ); 25 | const objectToQueryString = (queryParameters: Parameters) => { 26 | return queryParameters 27 | ? Object.entries(queryParameters).reduce( 28 | (queryString, [key, val], index) => { 29 | const symbol = queryString.length === 0 ? '?' : '&'; 30 | queryString += 31 | typeof val === 'string' ? `${symbol}${key}=${val}` : ''; 32 | return queryString; 33 | }, 34 | '' 35 | ) 36 | : ''; 37 | }; 38 | 39 | const App = () => { 40 | const param = getURLParameters(window.location.href); 41 | const cmRef = useRef(null); 42 | param.json = param.json ? decodeURI(param.json): undefined; 43 | const [code, setCode] = React.useState(decodeURIComponent(param.json || '')); 44 | const [json, setJson] = React.useState(); 45 | const [message, setMessage] = React.useState(''); 46 | const [linebar, setLinebar] = React.useState(''); 47 | 48 | const handleJson = useCallback(() => { 49 | setMessage(''); 50 | try { 51 | if (code) { 52 | const obj = JSON.parse(code); 53 | setJson(obj); 54 | } 55 | } catch (error) { 56 | if (error instanceof Error) { 57 | setMessage(error.message); 58 | setJson(undefined) 59 | } else { 60 | throw error; 61 | } 62 | } 63 | }, [code]); 64 | const formatJson = useCallback((_: any, replacer: number = 2) => { 65 | setMessage(''); 66 | try { 67 | if (code) { 68 | const obj = JSON.parse(code); 69 | const str = JSON.stringify(obj, null, replacer); 70 | setCode(str); 71 | } 72 | } catch (error) { 73 | if (error instanceof Error) { 74 | setMessage(error.message); 75 | setJson(undefined) 76 | } else { 77 | throw error; 78 | } 79 | } 80 | }, [code]); 81 | 82 | const shareJson = () => { 83 | param.json = encodeURI(code); 84 | history.push(`${objectToQueryString(param)}`, { some: "state" }); 85 | } 86 | 87 | useEffect(() => { 88 | handleJson() 89 | }, [code, handleJson]); 90 | 91 | const editor = ( 92 | 93 | 94 | { 101 | if (param.hidenheader === '1') { 102 | return; 103 | } 104 | const { selection } = cm.state; 105 | const line = cm.view.state.doc.lineAt(selection.main.from); 106 | setLinebar(`Line ${line.number}/${cm.state.doc.lines}, Column ${cm.state.selection.main.head - line.from + 1}`); 107 | const text = cm.state.sliceDoc(selection.main.from, selection.main.to); 108 | if (text) { 109 | if (selection.ranges.length > 1) { 110 | setLinebar(`${selection.ranges.length} selection regions`); 111 | } else { 112 | setLinebar(`${text.split('\n').length} lines, ${text.length} characters selected`); 113 | } 114 | } 115 | }} 116 | onChange={(value, viewUpdate) => { 117 | setCode(value) 118 | }} 119 | /> 120 | 121 | 122 | ); 123 | 124 | const preview = ( 125 | 126 | {message && ( 127 | 128 | {message} 129 | 130 | )} 131 | {json && typeof json == 'object' && ( 132 | 133 | )} 134 | 135 | ); 136 | return ( 137 | 138 | {!Number(param.corner) && ( 139 | 140 | )} 141 | 142 | {param.hidenheader !== '1' && ( 143 | 144 | JSON Viewer 145 | 146 | 147 | {linebar && ( 148 | {linebar} 149 | )} 150 | 151 | {message && ( 152 | {message} 153 | )} 154 | 155 | 156 | Format 157 | 158 | formatJson(null, 0)}> 159 | Compress 160 | 161 | {code && ( 162 | shareJson()}> 163 | Share 164 | 165 | )} 166 | 167 | 168 | 169 | )} 170 | 171 | {!param.view && editor} 172 | {!param.view && preview} 173 | {param.view === 'editor' && editor} 174 | {param.view === 'preview' && preview} 175 | 176 | 177 | 178 | ) 179 | }; 180 | 181 | export default App; 182 | -------------------------------------------------------------------------------- /src/app/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | body, html, #root { 12 | height: 100%; 13 | } -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './app/App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.module.less' { 4 | const classes: { readonly [key: string]: string }; 5 | export default classes; 6 | } 7 | 8 | declare module '*.less' { 9 | const classes: { readonly [key: string]: string }; 10 | export default classes; 11 | } 12 | 13 | declare module '*.svg' { 14 | import * as React from 'react'; 15 | 16 | export const ReactComponent: React.FunctionComponent>; 17 | 18 | const src: string; 19 | export default src; 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "jsx": "react-jsx", 6 | "declaration": true, 7 | "strict": true, 8 | "allowSyntheticDefaultImports": true, 9 | "esModuleInterop": true, 10 | "importHelpers": true, 11 | "noImplicitAny": true, 12 | "resolveJsonModule": true, 13 | "sourceMap": true, 14 | "baseUrl": "./src", 15 | "typeRoots": ["./node_modules/@types"], 16 | "lib": ["dom", "dom.iterable", "esnext"], 17 | "allowJs": true, 18 | "skipLibCheck": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "noFallthroughCasesInSwitch": true, 21 | "moduleResolution": "node", 22 | "isolatedModules": true, 23 | "noEmit": true 24 | }, 25 | "include": ["src", ".kktrc.ts"] 26 | } 27 | --------------------------------------------------------------------------------
128 | {message} 129 |